diff --git a/packages/frontend/core/src/components/affine/auth/ai-login-required.tsx b/packages/frontend/core/src/components/affine/auth/ai-login-required.tsx new file mode 100644 index 000000000000..7d4dd77ac219 --- /dev/null +++ b/packages/frontend/core/src/components/affine/auth/ai-login-required.tsx @@ -0,0 +1,41 @@ +import { useConfirmModal } from '@affine/component'; +import { authAtom } from '@affine/core/atoms'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { atom, useAtom, useSetAtom } from 'jotai'; +import { useCallback, useEffect } from 'react'; + +export const showAILoginRequiredAtom = atom(false); + +export const AiLoginRequiredModal = () => { + const t = useAFFiNEI18N(); + const [open, setOpen] = useAtom(showAILoginRequiredAtom); + const setAuth = useSetAtom(authAtom); + const { openConfirmModal, closeConfirmModal } = useConfirmModal(); + + const openSignIn = useCallback(() => { + setAuth(prev => ({ ...prev, openModal: true })); + }, [setAuth]); + + useEffect(() => { + if (open) { + openConfirmModal({ + title: t['com.affine.ai.login-required.dialog-title'](), + description: t['com.affine.ai.login-required.dialog-content'](), + onConfirm: () => { + setOpen(false); + openSignIn(); + }, + confirmButtonOptions: { + children: t['com.affine.ai.login-required.dialog-confirm'](), + type: 'primary', + }, + cancelText: t['com.affine.ai.login-required.dialog-cancel'](), + onOpenChange: setOpen, + }); + } else { + closeConfirmModal(); + } + }, [closeConfirmModal, open, openConfirmModal, openSignIn, setOpen, t]); + + return null; +}; diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/ai/copilot-client.ts b/packages/frontend/core/src/components/blocksuite/block-suite-editor/ai/copilot-client.ts index 8239badde8e9..4435c5b9ab29 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/ai/copilot-client.ts +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/ai/copilot-client.ts @@ -1,3 +1,4 @@ +import { showAILoginRequiredAtom } from '@affine/core/components/affine/auth/ai-login-required'; import { createCopilotMessageMutation, createCopilotSessionMutation, @@ -14,6 +15,7 @@ import { PaymentRequiredError, UnauthorizedError, } from '@blocksuite/blocks'; +import { getCurrentStore } from '@toeverything/infra'; type OptionsField = RequestOptions['variables'] extends { options: infer U } ? U : never; @@ -29,6 +31,7 @@ const fetcher = async ( switch (code) { case 401: + getCurrentStore().set(showAILoginRequiredAtom, true); throw new UnauthorizedError(); case 402: throw new PaymentRequiredError(); diff --git a/packages/frontend/core/src/providers/modal-provider.tsx b/packages/frontend/core/src/providers/modal-provider.tsx index 47168ef91d26..0de5b078b1b8 100644 --- a/packages/frontend/core/src/providers/modal-provider.tsx +++ b/packages/frontend/core/src/providers/modal-provider.tsx @@ -88,6 +88,12 @@ const HistoryTipsModal = lazy(() => })) ); +const AiLoginRequiredModal = lazy(() => + import('../components/affine/auth/ai-login-required').then(module => ({ + default: module.AiLoginRequiredModal, + })) +); + export const Setting = () => { const [{ open, workspaceMetadata, activeTab }, setOpenSettingModalAtom] = useAtom(openSettingModalAtom); @@ -206,6 +212,7 @@ export function CurrentWorkspaceModals() { {currentWorkspace?.flavour === WorkspaceFlavour.AFFINE_CLOUD && ( )} + ); } diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index f5235db815a3..05f8cc647d0d 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -1304,5 +1304,9 @@ "com.affine.ai-onboarding.local.action-dismiss": "Dismiss", "com.affine.ai-onboarding.local.action-learn-more": "Learn More", "com.affine.ai-onboarding.edgeless.title": "Meet AFFiNE AI", - "com.affine.ai-onboarding.edgeless.message": "Lets you think bigger, create faster, work smarter and save time for every project." + "com.affine.ai-onboarding.edgeless.message": "Lets you think bigger, create faster, work smarter and save time for every project.", + "com.affine.ai.login-required.dialog-title": "Sign in to Continue", + "com.affine.ai.login-required.dialog-content": "To use AFFiNE AI, please sign in to your AFFiNE Cloud account.", + "com.affine.ai.login-required.dialog-confirm": "Sign in", + "com.affine.ai.login-required.dialog-cancel": "Cancel" }