mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-23 13:02:21 +08:00
fix: sigin in different window may not refresh workspace list (#4270)
Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
@@ -7,11 +7,11 @@ import { WorkspaceFallback } from '@affine/component/workspace';
|
|||||||
import { CacheProvider } from '@emotion/react';
|
import { CacheProvider } from '@emotion/react';
|
||||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
import { getCurrentStore } from '@toeverything/infra/atom';
|
||||||
import { use } from 'foxact/use';
|
import { use } from 'foxact/use';
|
||||||
import { SessionProvider } from 'next-auth/react';
|
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
import { lazy, memo, Suspense } from 'react';
|
import { lazy, memo, Suspense } from 'react';
|
||||||
import { RouterProvider } from 'react-router-dom';
|
import { RouterProvider } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { CloudSessionProvider } from './providers/session-provider';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
import createEmotionCache from './utils/create-emotion-cache';
|
import createEmotionCache from './utils/create-emotion-cache';
|
||||||
|
|
||||||
@@ -48,9 +48,9 @@ const languageLoadingPromise = loadLanguage().catch(console.error);
|
|||||||
export const App = memo(function App() {
|
export const App = memo(function App() {
|
||||||
use(languageLoadingPromise);
|
use(languageLoadingPromise);
|
||||||
return (
|
return (
|
||||||
<SessionProvider refetchOnWindowFocus>
|
<CacheProvider value={cache}>
|
||||||
<CacheProvider value={cache}>
|
<AffineContext store={getCurrentStore()}>
|
||||||
<AffineContext store={getCurrentStore()}>
|
<CloudSessionProvider>
|
||||||
<DebugProvider>
|
<DebugProvider>
|
||||||
<RouterProvider
|
<RouterProvider
|
||||||
fallbackElement={<WorkspaceFallback key="RouterFallback" />}
|
fallbackElement={<WorkspaceFallback key="RouterFallback" />}
|
||||||
@@ -58,8 +58,8 @@ export const App = memo(function App() {
|
|||||||
future={future}
|
future={future}
|
||||||
/>
|
/>
|
||||||
</DebugProvider>
|
</DebugProvider>
|
||||||
</AffineContext>
|
</CloudSessionProvider>
|
||||||
</CacheProvider>
|
</AffineContext>
|
||||||
</SessionProvider>
|
</CacheProvider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ import {
|
|||||||
AuthModal as AuthModalBase,
|
AuthModal as AuthModalBase,
|
||||||
type AuthModalProps as AuthModalBaseProps,
|
type AuthModalProps as AuthModalBaseProps,
|
||||||
} from '@affine/component/auth-components';
|
} from '@affine/component/auth-components';
|
||||||
import { refreshRootMetadataAtom } from '@affine/workspace/atom';
|
import { type FC, useCallback, useMemo } from 'react';
|
||||||
import { useSetAtom } from 'jotai';
|
|
||||||
import { type FC, startTransition, useCallback, useMemo } from 'react';
|
|
||||||
|
|
||||||
import { AfterSignInSendEmail } from './after-sign-in-send-email';
|
import { AfterSignInSendEmail } from './after-sign-in-send-email';
|
||||||
import { AfterSignUpSendEmail } from './after-sign-up-send-email';
|
import { AfterSignUpSendEmail } from './after-sign-up-send-email';
|
||||||
@@ -60,14 +58,9 @@ export const AuthModal: FC<AuthModalBaseProps & AuthProps> = ({
|
|||||||
setEmailType,
|
setEmailType,
|
||||||
emailType,
|
emailType,
|
||||||
}) => {
|
}) => {
|
||||||
const refreshMetadata = useSetAtom(refreshRootMetadataAtom);
|
|
||||||
|
|
||||||
const onSignedIn = useCallback(() => {
|
const onSignedIn = useCallback(() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
startTransition(() => {
|
}, [setOpen]);
|
||||||
refreshMetadata();
|
|
||||||
});
|
|
||||||
}, [refreshMetadata, setOpen]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthModalBase open={open} setOpen={setOpen}>
|
<AuthModalBase open={open} setOpen={setOpen}>
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ import {
|
|||||||
BackButton,
|
BackButton,
|
||||||
ModalHeader,
|
ModalHeader,
|
||||||
} from '@affine/component/auth-components';
|
} from '@affine/component/auth-components';
|
||||||
import { pushNotificationAtom } from '@affine/component/notification-center';
|
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { Button } from '@toeverything/components/button';
|
import { Button } from '@toeverything/components/button';
|
||||||
import { useSetAtom } from 'jotai';
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
@@ -25,8 +23,6 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
|
|||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const { update } = useSession();
|
const { update } = useSession();
|
||||||
|
|
||||||
const pushNotification = useSetAtom(pushNotificationAtom);
|
|
||||||
|
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [passwordError, setPasswordError] = useState(false);
|
const [passwordError, setPasswordError] = useState(false);
|
||||||
|
|
||||||
@@ -43,12 +39,7 @@ export const SignInWithPassword: FC<AuthPanelProps> = ({
|
|||||||
|
|
||||||
await update();
|
await update();
|
||||||
onSignedIn?.();
|
onSignedIn?.();
|
||||||
pushNotification({
|
}, [email, password, onSignedIn, update]);
|
||||||
title: t['com.affine.auth.has.signed'](),
|
|
||||||
message: t['com.affine.auth.has.signed.message'](),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
}, [email, password, pushNotification, onSignedIn, t, update]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import { Modal, ModalWrapper } from '@affine/component';
|
|
||||||
import { Trans } from '@affine/i18n';
|
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
|
||||||
|
|
||||||
import * as styles from './styles.css';
|
|
||||||
|
|
||||||
export const DesktopLoginModal = ({
|
|
||||||
signingEmail,
|
|
||||||
}: {
|
|
||||||
signingEmail?: string;
|
|
||||||
}) => {
|
|
||||||
const t = useAFFiNEI18N();
|
|
||||||
return (
|
|
||||||
<Modal open={!!signingEmail}>
|
|
||||||
<ModalWrapper className={styles.root}>
|
|
||||||
<div className={styles.title}>
|
|
||||||
{t['com.affine.auth.desktop.signing.in']()}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Trans i18nKey="com.affine.auth.desktop.signing.in.message">
|
|
||||||
Signing in with account {signingEmail}
|
|
||||||
</Trans>
|
|
||||||
</ModalWrapper>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { style } from '@vanilla-extract/css';
|
|
||||||
|
|
||||||
export const root = style({
|
|
||||||
padding: '20px 24px',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const title = style({
|
|
||||||
fontSize: 'var(--affine-font-h-6)',
|
|
||||||
fontWeight: 600,
|
|
||||||
marginBottom: 12,
|
|
||||||
});
|
|
||||||
@@ -4,7 +4,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
|||||||
import { fetcher } from '@affine/workspace/affine/gql';
|
import { fetcher } from '@affine/workspace/affine/gql';
|
||||||
import { Logo1Icon } from '@blocksuite/icons';
|
import { Logo1Icon } from '@blocksuite/icons';
|
||||||
import { Button } from '@toeverything/components/button';
|
import { Button } from '@toeverything/components/button';
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
type LoaderFunction,
|
type LoaderFunction,
|
||||||
useLoaderData,
|
useLoaderData,
|
||||||
@@ -72,15 +72,12 @@ const OpenAppImpl = ({ urlToOpen, channel }: OpenAppProps) => {
|
|||||||
const [params] = useSearchParams();
|
const [params] = useSearchParams();
|
||||||
const autoOpen = useMemo(() => params.get('open') !== 'false', [params]);
|
const autoOpen = useMemo(() => params.get('open') !== 'false', [params]);
|
||||||
|
|
||||||
useEffect(() => {
|
if (urlToOpen && lastOpened !== urlToOpen && autoOpen) {
|
||||||
if (!urlToOpen || lastOpened === urlToOpen || !autoOpen) {
|
lastOpened = urlToOpen;
|
||||||
return;
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
lastOpened = urlToOpen;
|
|
||||||
open(urlToOpen, '_blank');
|
open(urlToOpen, '_blank');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}, [urlToOpen, autoOpen]);
|
}
|
||||||
|
|
||||||
if (!urlToOpen) {
|
if (!urlToOpen) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import { pushNotificationAtom } from '@affine/component/notification-center';
|
|
||||||
import { isDesktop } from '@affine/env/constant';
|
|
||||||
import { WorkspaceSubPath } from '@affine/env/workspace';
|
import { WorkspaceSubPath } from '@affine/env/workspace';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
|
||||||
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
||||||
import { assertExists } from '@blocksuite/global/utils';
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
import { arrayMove } from '@dnd-kit/sortable';
|
import { arrayMove } from '@dnd-kit/sortable';
|
||||||
@@ -16,8 +13,6 @@ import {
|
|||||||
startTransition,
|
startTransition,
|
||||||
Suspense,
|
Suspense,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
useTransition,
|
useTransition,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
@@ -43,12 +38,6 @@ const Auth = lazy(() =>
|
|||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
const DesktopLogin = lazy(() =>
|
|
||||||
import('../components/affine/desktop-login-modal').then(module => ({
|
|
||||||
default: module.DesktopLoginModal,
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
const WorkspaceListModal = lazy(() =>
|
const WorkspaceListModal = lazy(() =>
|
||||||
import('../components/pure/workspace-list-modal').then(module => ({
|
import('../components/pure/workspace-list-modal').then(module => ({
|
||||||
default: module.WorkspaceListModal,
|
default: module.WorkspaceListModal,
|
||||||
@@ -146,49 +135,6 @@ export const AuthModal = (): ReactElement => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DesktopLoginModal = (): ReactElement => {
|
|
||||||
const [signingEmail, setSigningEmail] = useState<string>();
|
|
||||||
const setAuthAtom = useSetAtom(authAtom);
|
|
||||||
const pushNotification = useSetAtom(pushNotificationAtom);
|
|
||||||
const t = useAFFiNEI18N();
|
|
||||||
|
|
||||||
// hack for closing the potentially opened auth modal
|
|
||||||
const closeAuthModal = useCallback(() => {
|
|
||||||
setAuthAtom(prev => ({ ...prev, openModal: false }));
|
|
||||||
}, [setAuthAtom]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return window.events?.ui.onStartLogin(opts => {
|
|
||||||
setSigningEmail(opts.email);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return window.events?.ui.onFinishLogin(({ success, email }) => {
|
|
||||||
if (email && email !== signingEmail) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setSigningEmail(undefined);
|
|
||||||
closeAuthModal();
|
|
||||||
if (success) {
|
|
||||||
pushNotification({
|
|
||||||
title: t['com.affine.auth.toast.title.signed-in'](),
|
|
||||||
message: t['com.affine.auth.toast.message.signed-in'](),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
pushNotification({
|
|
||||||
title: t['com.affine.auth.toast.title.failed'](),
|
|
||||||
message: t['com.affine.auth.toast.message.failed'](),
|
|
||||||
type: 'error',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [closeAuthModal, pushNotification, signingEmail, t]);
|
|
||||||
|
|
||||||
return <DesktopLogin signingEmail={signingEmail} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CurrentWorkspaceModals() {
|
export function CurrentWorkspaceModals() {
|
||||||
const [currentWorkspace] = useCurrentWorkspace();
|
const [currentWorkspace] = useCurrentWorkspace();
|
||||||
const [openDisableCloudAlertModal, setOpenDisableCloudAlertModal] = useAtom(
|
const [openDisableCloudAlertModal, setOpenDisableCloudAlertModal] = useAtom(
|
||||||
@@ -323,7 +269,6 @@ export const AllWorkspaceModals = (): ReactElement => {
|
|||||||
<Suspense>
|
<Suspense>
|
||||||
<AuthModal />
|
<AuthModal />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
{isDesktop && <DesktopLoginModal />}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import '@toeverything/hooks/use-affine-ipc-renderer';
|
||||||
|
|
||||||
|
import { pushNotificationAtom } from '@affine/component/notification-center';
|
||||||
|
import { isDesktop } from '@affine/env/constant';
|
||||||
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
import { refreshRootMetadataAtom } from '@affine/workspace/atom';
|
||||||
|
import { useSetAtom } from 'jotai';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||||
|
import { SessionProvider, useSession } from 'next-auth/react';
|
||||||
|
import { type PropsWithChildren, startTransition, useRef } from 'react';
|
||||||
|
|
||||||
|
const SessionReporter = () => {
|
||||||
|
const session = useSession();
|
||||||
|
const prevSession = useRef<ReturnType<typeof useSession>>();
|
||||||
|
const pushNotification = useSetAtom(pushNotificationAtom);
|
||||||
|
const refreshMetadata = useSetAtom(refreshRootMetadataAtom);
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
|
||||||
|
if (prevSession.current !== session && session.status !== 'loading') {
|
||||||
|
// unauthenticated -> authenticated
|
||||||
|
if (
|
||||||
|
prevSession.current?.status === 'unauthenticated' &&
|
||||||
|
session.status === 'authenticated'
|
||||||
|
) {
|
||||||
|
startTransition(() => {
|
||||||
|
refreshMetadata();
|
||||||
|
});
|
||||||
|
pushNotification({
|
||||||
|
title: t['com.affine.auth.has.signed'](),
|
||||||
|
message: t['com.affine.auth.has.signed.message'](),
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isDesktop) {
|
||||||
|
window.affine.ipcRenderer.send('affine:login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prevSession.current = session;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CloudSessionProvider = ({ children }: PropsWithChildren) => {
|
||||||
|
return (
|
||||||
|
<SessionProvider refetchOnWindowFocus>
|
||||||
|
<SessionReporter />
|
||||||
|
{children}
|
||||||
|
</SessionProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
import type { App } from 'electron';
|
import { type App, type BrowserWindow, ipcMain } from 'electron';
|
||||||
|
|
||||||
import { buildType, CLOUD_BASE_URL, isDev } from './config';
|
import { buildType, CLOUD_BASE_URL, isDev } from './config';
|
||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
restoreOrCreateWindow,
|
restoreOrCreateWindow,
|
||||||
setCookie,
|
setCookie,
|
||||||
} from './main-window';
|
} from './main-window';
|
||||||
import { uiSubjects } from './ui';
|
|
||||||
|
|
||||||
let protocol = buildType === 'stable' ? 'affine' : `affine-${buildType}`;
|
let protocol = buildType === 'stable' ? 'affine' : `affine-${buildType}`;
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
@@ -100,16 +99,16 @@ async function handleOauthJwt(url: string) {
|
|||||||
isSecure ? '__Secure-next-auth.callback-url' : 'next-auth.callback-url'
|
isSecure ? '__Secure-next-auth.callback-url' : 'next-auth.callback-url'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let hiddenWindow: BrowserWindow | null = null;
|
||||||
|
|
||||||
|
ipcMain.once('affine:login', () => {
|
||||||
|
hiddenWindow?.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
// hacks to refresh auth state in the main window
|
// hacks to refresh auth state in the main window
|
||||||
const window = await handleOpenUrlInHiddenWindow(
|
hiddenWindow = await handleOpenUrlInHiddenWindow(
|
||||||
mainWindowOrigin + '/auth/signIn'
|
mainWindowOrigin + '/auth/signIn'
|
||||||
);
|
);
|
||||||
uiSubjects.onFinishLogin.next({
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
window.destroy();
|
|
||||||
}, 3000);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('failed to open url in popup', e);
|
logger.error('failed to open url in popup', e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,6 @@ import { uiSubjects } from './subject';
|
|||||||
* Events triggered by application menu
|
* Events triggered by application menu
|
||||||
*/
|
*/
|
||||||
export const uiEvents = {
|
export const uiEvents = {
|
||||||
onFinishLogin: (
|
|
||||||
fn: (result: { success: boolean; email?: string }) => void
|
|
||||||
) => {
|
|
||||||
const sub = uiSubjects.onFinishLogin.subscribe(fn);
|
|
||||||
return () => {
|
|
||||||
sub.unsubscribe();
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onStartLogin: (fn: (opts: { email?: string }) => void) => {
|
|
||||||
const sub = uiSubjects.onStartLogin.subscribe(fn);
|
|
||||||
return () => {
|
|
||||||
sub.unsubscribe();
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onMaximized: (fn: (maximized: boolean) => void) => {
|
onMaximized: (fn: (maximized: boolean) => void) => {
|
||||||
const sub = uiSubjects.onMaximized.subscribe(fn);
|
const sub = uiSubjects.onMaximized.subscribe(fn);
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
export const uiSubjects = {
|
export const uiSubjects = {
|
||||||
onStartLogin: new Subject<{ email?: string }>(),
|
|
||||||
onFinishLogin: new Subject<{ success: boolean; email?: string }>(),
|
|
||||||
onMaximized: new Subject<boolean>(),
|
onMaximized: new Subject<boolean>(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -266,10 +266,6 @@ export interface WorkspaceEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UIEvents {
|
export interface UIEvents {
|
||||||
onStartLogin: (fn: (options: { email?: string }) => void) => () => void;
|
|
||||||
onFinishLogin: (
|
|
||||||
fn: (result: { success: boolean; email?: string }) => void
|
|
||||||
) => () => void;
|
|
||||||
onMaximized: (fn: (maximized: boolean) => void) => () => void;
|
onMaximized: (fn: (maximized: boolean) => void) => () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user