From e01a7f6215e7bf90bbf4fecb5044267d0f2bb5f2 Mon Sep 17 00:00:00 2001 From: Jimmfly Date: Fri, 20 Dec 2024 12:27:10 +0800 Subject: [PATCH] feat(core): add version check to sign in --- .../sign-in/sign-in-with-password.tsx | 22 +++++++++++++++- .../src/desktop/pages/auth/magic-link.tsx | 19 +++++++++++--- .../src/desktop/pages/auth/oauth-callback.tsx | 20 +++++++++++--- .../core/src/desktop/pages/auth/sign-in.tsx | 26 ++++++++++++++++++- .../i18n/src/i18n-completenesses.json | 6 ++--- packages/frontend/i18n/src/resources/en.json | 7 ++++- 6 files changed, 88 insertions(+), 12 deletions(-) diff --git a/packages/frontend/core/src/components/sign-in/sign-in-with-password.tsx b/packages/frontend/core/src/components/sign-in/sign-in-with-password.tsx index 30b00e1c5859f..33dc21c43b6b4 100644 --- a/packages/frontend/core/src/components/sign-in/sign-in-with-password.tsx +++ b/packages/frontend/core/src/components/sign-in/sign-in-with-password.tsx @@ -9,14 +9,20 @@ import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hoo import { AuthService, CaptchaService, + isBackendError, ServerService, } from '@affine/core/modules/cloud'; import { Unreachable } from '@affine/env/constant'; -import { ServerDeploymentType } from '@affine/graphql'; +import { + ErrorNames, + ServerDeploymentType, + UserFriendlyError, +} from '@affine/graphql'; import { useI18n } from '@affine/i18n'; import { useLiveData, useService } from '@toeverything/infra'; import type { Dispatch, SetStateAction } from 'react'; import { useCallback, useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import type { SignInState } from '.'; import { Captcha } from './captcha'; @@ -59,6 +65,7 @@ export const SignInWithPasswordStep = ({ const [isLoading, setIsLoading] = useState(false); const loginStatus = useLiveData(authService.session.status$); + const nav = useNavigate(); useEffect(() => { if (loginStatus === 'authenticated') { @@ -84,6 +91,18 @@ export const SignInWithPasswordStep = ({ }); } catch (err) { console.error(err); + if ( + err instanceof Error && + isBackendError(err) && + UserFriendlyError.fromAnyError(err).name === + ErrorNames.UNSUPPORTED_CLIENT_VERSION + ) { + const { action } = UserFriendlyError.fromAnyError(err).args; + nav( + `/sign-in?error=${encodeURIComponent(err.message)}&action=${encodeURIComponent(action as string)}` + ); + return; + } setPasswordError(true); } finally { setIsLoading(false); @@ -97,6 +116,7 @@ export const SignInWithPasswordStep = ({ email, password, challenge, + nav, ]); const sendMagicLink = useCallback(() => { diff --git a/packages/frontend/core/src/desktop/pages/auth/magic-link.tsx b/packages/frontend/core/src/desktop/pages/auth/magic-link.tsx index ce8ba88e1cb98..1587b03c7b547 100644 --- a/packages/frontend/core/src/desktop/pages/auth/magic-link.tsx +++ b/packages/frontend/core/src/desktop/pages/auth/magic-link.tsx @@ -1,3 +1,4 @@ +import { ErrorNames, UserFriendlyError } from '@affine/graphql'; import { useService } from '@toeverything/infra'; import { useEffect, useRef } from 'react'; import { @@ -8,7 +9,7 @@ import { useNavigate, } from 'react-router-dom'; -import { AuthService } from '../../../modules/cloud'; +import { AuthService, isBackendError } from '../../../modules/cloud'; import { supportedClient } from './common'; interface LoaderData { @@ -78,8 +79,20 @@ export const Component = () => { } }); }) - .catch(e => { - nav(`/sign-in?error=${encodeURIComponent(e.message)}`); + .catch(err => { + if ( + err instanceof Error && + isBackendError(err) && + UserFriendlyError.fromAnyError(err).name === + ErrorNames.UNSUPPORTED_CLIENT_VERSION + ) { + const { action } = UserFriendlyError.fromAnyError(err).args; + nav( + `/sign-in?error=${encodeURIComponent(err.message)}&action=${encodeURIComponent(action as string)}` + ); + return; + } + nav(`/sign-in?error=${encodeURIComponent(err.message)}`); }); }, [auth, data, data.email, data.redirectUri, data.token, nav]); diff --git a/packages/frontend/core/src/desktop/pages/auth/oauth-callback.tsx b/packages/frontend/core/src/desktop/pages/auth/oauth-callback.tsx index 453f4a6e326cf..b2df33351ac01 100644 --- a/packages/frontend/core/src/desktop/pages/auth/oauth-callback.tsx +++ b/packages/frontend/core/src/desktop/pages/auth/oauth-callback.tsx @@ -1,3 +1,4 @@ +import { ErrorNames, UserFriendlyError } from '@affine/graphql'; import { useService } from '@toeverything/infra'; import { useEffect, useRef } from 'react'; import { @@ -8,7 +9,7 @@ import { useNavigate, } from 'react-router-dom'; -import { AuthService } from '../../../modules/cloud'; +import { AuthService, isBackendError } from '../../../modules/cloud'; import { supportedClient } from './common'; interface LoaderData { @@ -80,8 +81,21 @@ export const Component = () => { // TODO(@forehalo): need a good way to go back to previous tab and close current one nav(redirectUri ?? '/'); }) - .catch(e => { - nav(`/sign-in?error=${encodeURIComponent(e.message)}`); + .catch(err => { + if ( + err instanceof Error && + isBackendError(err) && + UserFriendlyError.fromAnyError(err).name === + ErrorNames.UNSUPPORTED_CLIENT_VERSION + ) { + const { action } = UserFriendlyError.fromAnyError(err).args; + nav( + `/sign-in?error=${encodeURIComponent(err.message)}&action=${encodeURIComponent(action as string)}` + ); + return; + } + nav(`/sign-in?error=${encodeURIComponent(err.message)}`); + return; }); }, [data, auth, nav]); diff --git a/packages/frontend/core/src/desktop/pages/auth/sign-in.tsx b/packages/frontend/core/src/desktop/pages/auth/sign-in.tsx index f58c25e2f6e92..64675076d86db 100644 --- a/packages/frontend/core/src/desktop/pages/auth/sign-in.tsx +++ b/packages/frontend/core/src/desktop/pages/auth/sign-in.tsx @@ -1,4 +1,4 @@ -import { notify } from '@affine/component'; +import { notify, useConfirmModal } from '@affine/component'; import { AffineOtherPageLayout } from '@affine/component/affine-other-page-layout'; import { SignInPageContainer } from '@affine/component/auth-components'; import { SignInPanel } from '@affine/core/components/sign-in'; @@ -24,8 +24,10 @@ export const SignIn = ({ const navigate = useNavigate(); const { jumpToIndex } = useNavigateHelper(); const [searchParams] = useSearchParams(); + const { openConfirmModal } = useConfirmModal(); const redirectUrl = redirectUrlFromProps ?? searchParams.get('redirect_uri'); const error = searchParams.get('error'); + const action = searchParams.get('action'); useEffect(() => { if (error) { @@ -36,6 +38,28 @@ export const SignIn = ({ } }, [error, t]); + useEffect(() => { + if (action === 'upgrade' || action === 'downgrade') { + openConfirmModal({ + title: t['com.affine.minimum-client.title'](), + description: + t[ + `com.affine.minimum-client.${action === 'upgrade' ? 'outdated' : 'advanced'}.message` + ](), + confirmText: + t[ + `com.affine.minimum-client.${action === 'upgrade' ? 'outdated' : 'advanced'}.button` + ](), + onConfirm: () => + window.open( + BUILD_CONFIG.downloadUrl, + '_blank', + 'noreferrer noopener' + ), + }); + } + }, [action, jumpToIndex, openConfirmModal, searchParams, t]); + const handleClose = () => { if (session.status$.value === 'authenticated' && redirectUrl) { navigate(redirectUrl, { diff --git a/packages/frontend/i18n/src/i18n-completenesses.json b/packages/frontend/i18n/src/i18n-completenesses.json index 1c81c85c44084..87281f22cbd75 100644 --- a/packages/frontend/i18n/src/i18n-completenesses.json +++ b/packages/frontend/i18n/src/i18n-completenesses.json @@ -1,5 +1,5 @@ { - "ar": 68, + "ar": 67, "ca": 5, "da": 5, "de": 26, @@ -15,10 +15,10 @@ "ja": 90, "ko": 72, "pl": 0, - "pt-BR": 78, + "pt-BR": 77, "ru": 66, "sv-SE": 4, "ur": 2, - "zh-Hans": 91, + "zh-Hans": 90, "zh-Hant": 90 } \ No newline at end of file diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index f112ce394a980..e3b03ab0e252f 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -1614,5 +1614,10 @@ "com.affine.payment.sync-paused.member.both.description": "This workspace has exceeded both storage and member limits, causing synchronization to pause. Please contact your workspace owner to address these limits and resume syncing.", "com.affine.payment.sync-paused.member.storage.description": "This workspace has exceeded its storage limit and synchronization has been paused. Please contact your workspace owner to either reduce storage usage or upgrade the plan to resume syncing.", "com.affine.payment.sync-paused.member.member.description": "This workspace has reached its maximum member capacity and synchronization has been paused. Please contact your workspace owner to either adjust team membership or upgrade the plan to resume syncing.", - "com.affine.payment.sync-paused.member.member.confirm": "Got It" + "com.affine.payment.sync-paused.member.member.confirm": "Got It", + "com.affine.minimum-client.title": "Client is outdated", + "com.affine.minimum-client.outdated.message": "This client is outdated, for the security of your data, please update the client, before accessing the data, or visit our web app.", + "com.affine.minimum-client.advanced.message": "This client app is not supported by the server yet, for the security of your data, please download the matching client, before accessing the data, or visit our web app.", + "com.affine.minimum-client.outdated.button": "Update client", + "com.affine.minimum-client.advanced.button": "Download client" }