diff --git a/apps/core/src/app.tsx b/apps/core/src/app.tsx index 920b461436..a3a007019b 100644 --- a/apps/core/src/app.tsx +++ b/apps/core/src/app.tsx @@ -7,11 +7,11 @@ import { WorkspaceFallback } from '@affine/component/workspace'; import { CacheProvider } from '@emotion/react'; import { getCurrentStore } from '@toeverything/infra/atom'; import { use } from 'foxact/use'; -import { SessionProvider } from 'next-auth/react'; import type { PropsWithChildren, ReactElement } from 'react'; import { lazy, memo, Suspense } from 'react'; import { RouterProvider } from 'react-router-dom'; +import { CloudSessionProvider } from './providers/session-provider'; import { router } from './router'; import createEmotionCache from './utils/create-emotion-cache'; @@ -48,9 +48,9 @@ const languageLoadingPromise = loadLanguage().catch(console.error); export const App = memo(function App() { use(languageLoadingPromise); return ( - - - + + + } @@ -58,8 +58,8 @@ export const App = memo(function App() { future={future} /> - - - + + + ); }); diff --git a/apps/core/src/components/affine/auth/index.tsx b/apps/core/src/components/affine/auth/index.tsx index 128480b3e7..7a6660e285 100644 --- a/apps/core/src/components/affine/auth/index.tsx +++ b/apps/core/src/components/affine/auth/index.tsx @@ -2,9 +2,7 @@ import { AuthModal as AuthModalBase, type AuthModalProps as AuthModalBaseProps, } from '@affine/component/auth-components'; -import { refreshRootMetadataAtom } from '@affine/workspace/atom'; -import { useSetAtom } from 'jotai'; -import { type FC, startTransition, useCallback, useMemo } from 'react'; +import { type FC, useCallback, useMemo } from 'react'; import { AfterSignInSendEmail } from './after-sign-in-send-email'; import { AfterSignUpSendEmail } from './after-sign-up-send-email'; @@ -60,14 +58,9 @@ export const AuthModal: FC = ({ setEmailType, emailType, }) => { - const refreshMetadata = useSetAtom(refreshRootMetadataAtom); - const onSignedIn = useCallback(() => { setOpen(false); - startTransition(() => { - refreshMetadata(); - }); - }, [refreshMetadata, setOpen]); + }, [setOpen]); return ( diff --git a/apps/core/src/components/affine/auth/sign-in-with-password.tsx b/apps/core/src/components/affine/auth/sign-in-with-password.tsx index 776bac8edb..5559e70433 100644 --- a/apps/core/src/components/affine/auth/sign-in-with-password.tsx +++ b/apps/core/src/components/affine/auth/sign-in-with-password.tsx @@ -4,10 +4,8 @@ import { BackButton, ModalHeader, } from '@affine/component/auth-components'; -import { pushNotificationAtom } from '@affine/component/notification-center'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { Button } from '@toeverything/components/button'; -import { useSetAtom } from 'jotai'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { useSession } from 'next-auth/react'; import type { FC } from 'react'; @@ -25,8 +23,6 @@ export const SignInWithPassword: FC = ({ const t = useAFFiNEI18N(); const { update } = useSession(); - const pushNotification = useSetAtom(pushNotificationAtom); - const [password, setPassword] = useState(''); const [passwordError, setPasswordError] = useState(false); @@ -43,12 +39,7 @@ export const SignInWithPassword: FC = ({ await update(); onSignedIn?.(); - pushNotification({ - title: t['com.affine.auth.has.signed'](), - message: t['com.affine.auth.has.signed.message'](), - type: 'success', - }); - }, [email, password, pushNotification, onSignedIn, t, update]); + }, [email, password, onSignedIn, update]); return ( <> diff --git a/apps/core/src/components/affine/desktop-login-modal/index.tsx b/apps/core/src/components/affine/desktop-login-modal/index.tsx deleted file mode 100644 index 824816b405..0000000000 --- a/apps/core/src/components/affine/desktop-login-modal/index.tsx +++ /dev/null @@ -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 ( - - -
- {t['com.affine.auth.desktop.signing.in']()} -
- - - Signing in with account {signingEmail} - -
-
- ); -}; diff --git a/apps/core/src/components/affine/desktop-login-modal/styles.css.ts b/apps/core/src/components/affine/desktop-login-modal/styles.css.ts deleted file mode 100644 index 0899875b02..0000000000 --- a/apps/core/src/components/affine/desktop-login-modal/styles.css.ts +++ /dev/null @@ -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, -}); diff --git a/apps/core/src/pages/open-app.tsx b/apps/core/src/pages/open-app.tsx index 1b44c8e337..59249d4445 100644 --- a/apps/core/src/pages/open-app.tsx +++ b/apps/core/src/pages/open-app.tsx @@ -4,7 +4,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { fetcher } from '@affine/workspace/affine/gql'; import { Logo1Icon } from '@blocksuite/icons'; import { Button } from '@toeverything/components/button'; -import { useCallback, useEffect, useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { type LoaderFunction, useLoaderData, @@ -72,15 +72,12 @@ const OpenAppImpl = ({ urlToOpen, channel }: OpenAppProps) => { const [params] = useSearchParams(); const autoOpen = useMemo(() => params.get('open') !== 'false', [params]); - useEffect(() => { - if (!urlToOpen || lastOpened === urlToOpen || !autoOpen) { - return; - } + if (urlToOpen && lastOpened !== urlToOpen && autoOpen) { + lastOpened = urlToOpen; setTimeout(() => { - lastOpened = urlToOpen; open(urlToOpen, '_blank'); }, 1000); - }, [urlToOpen, autoOpen]); + } if (!urlToOpen) { return null; diff --git a/apps/core/src/providers/modal-provider.tsx b/apps/core/src/providers/modal-provider.tsx index db36ca4757..3d3c9a09a6 100644 --- a/apps/core/src/providers/modal-provider.tsx +++ b/apps/core/src/providers/modal-provider.tsx @@ -1,7 +1,4 @@ -import { pushNotificationAtom } from '@affine/component/notification-center'; -import { isDesktop } from '@affine/env/constant'; import { WorkspaceSubPath } from '@affine/env/workspace'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom'; import { assertExists } from '@blocksuite/global/utils'; import { arrayMove } from '@dnd-kit/sortable'; @@ -16,8 +13,6 @@ import { startTransition, Suspense, useCallback, - useEffect, - useState, useTransition, } 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(() => import('../components/pure/workspace-list-modal').then(module => ({ default: module.WorkspaceListModal, @@ -146,49 +135,6 @@ export const AuthModal = (): ReactElement => { ); }; -export const DesktopLoginModal = (): ReactElement => { - const [signingEmail, setSigningEmail] = useState(); - 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 ; -}; - export function CurrentWorkspaceModals() { const [currentWorkspace] = useCurrentWorkspace(); const [openDisableCloudAlertModal, setOpenDisableCloudAlertModal] = useAtom( @@ -323,7 +269,6 @@ export const AllWorkspaceModals = (): ReactElement => { - {isDesktop && } ); }; diff --git a/apps/core/src/providers/session-provider.tsx b/apps/core/src/providers/session-provider.tsx new file mode 100644 index 0000000000..9dc25640c3 --- /dev/null +++ b/apps/core/src/providers/session-provider.tsx @@ -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>(); + 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 ( + + + {children} + + ); +}; diff --git a/apps/electron/src/main/deep-link.ts b/apps/electron/src/main/deep-link.ts index cada04f5b3..3c77e83d3c 100644 --- a/apps/electron/src/main/deep-link.ts +++ b/apps/electron/src/main/deep-link.ts @@ -1,6 +1,6 @@ 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 { logger } from './logger'; @@ -11,7 +11,6 @@ import { restoreOrCreateWindow, setCookie, } from './main-window'; -import { uiSubjects } from './ui'; let protocol = buildType === 'stable' ? 'affine' : `affine-${buildType}`; if (isDev) { @@ -100,16 +99,16 @@ async function handleOauthJwt(url: string) { 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 - const window = await handleOpenUrlInHiddenWindow( + hiddenWindow = await handleOpenUrlInHiddenWindow( mainWindowOrigin + '/auth/signIn' ); - uiSubjects.onFinishLogin.next({ - success: true, - }); - setTimeout(() => { - window.destroy(); - }, 3000); } catch (e) { logger.error('failed to open url in popup', e); } diff --git a/apps/electron/src/main/ui/events.ts b/apps/electron/src/main/ui/events.ts index bc147dcc35..be8f4225e4 100644 --- a/apps/electron/src/main/ui/events.ts +++ b/apps/electron/src/main/ui/events.ts @@ -5,20 +5,6 @@ import { uiSubjects } from './subject'; * Events triggered by application menu */ 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) => { const sub = uiSubjects.onMaximized.subscribe(fn); return () => { diff --git a/apps/electron/src/main/ui/subject.ts b/apps/electron/src/main/ui/subject.ts index 3e42ef9e5b..bfd6fae9e8 100644 --- a/apps/electron/src/main/ui/subject.ts +++ b/apps/electron/src/main/ui/subject.ts @@ -1,7 +1,5 @@ import { Subject } from 'rxjs'; export const uiSubjects = { - onStartLogin: new Subject<{ email?: string }>(), - onFinishLogin: new Subject<{ success: boolean; email?: string }>(), onMaximized: new Subject(), }; diff --git a/packages/infra/src/type.ts b/packages/infra/src/type.ts index 53563edbda..81d69124a3 100644 --- a/packages/infra/src/type.ts +++ b/packages/infra/src/type.ts @@ -266,10 +266,6 @@ export interface WorkspaceEvents { } 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; }