From 41cea88b5711f574453d0a4454a12c77700f9a4e Mon Sep 17 00:00:00 2001 From: Jimmfly Date: Mon, 23 Dec 2024 16:31:27 +0800 Subject: [PATCH] feat(core): catch unsupported client version error --- .../core/src/components/sign-in/sign-in.tsx | 43 ++++++++++++++++--- .../src/desktop/pages/auth/magic-link.tsx | 14 +----- .../src/desktop/pages/auth/oauth-callback.tsx | 15 +------ .../core/src/modules/cloud/stores/auth.ts | 9 ++++ .../desktop-api/service/desktop-api.ts | 36 ++++++++++++++-- packages/frontend/graphql/src/error.ts | 4 +- 6 files changed, 84 insertions(+), 37 deletions(-) diff --git a/packages/frontend/core/src/components/sign-in/sign-in.tsx b/packages/frontend/core/src/components/sign-in/sign-in.tsx index 57d13924aa1cb..5dc5828eba901 100644 --- a/packages/frontend/core/src/components/sign-in/sign-in.tsx +++ b/packages/frontend/core/src/components/sign-in/sign-in.tsx @@ -4,7 +4,11 @@ import { OAuth } from '@affine/core/components/affine/auth/oauth'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { AuthService, ServerService } from '@affine/core/modules/cloud'; import { FeatureFlagService } from '@affine/core/modules/feature-flag'; -import { ServerDeploymentType } from '@affine/graphql'; +import { + ErrorNames, + ServerDeploymentType, + UserFriendlyError, +} from '@affine/graphql'; import { Trans, useI18n } from '@affine/i18n'; import { ArrowRightBigIcon, PublishIcon } from '@blocksuite/icons/rc'; import { useLiveData, useService } from '@toeverything/infra'; @@ -120,14 +124,41 @@ export const SignInStep = ({ } catch (err) { console.error(err); - // TODO(@eyhn): better error handling - notify.error({ - title: 'Failed to send email. Please try again.', - }); + const userFriendlyError = UserFriendlyError.fromAnyError(err); + if (userFriendlyError.name === ErrorNames.UNSUPPORTED_CLIENT_VERSION) { + const { action } = userFriendlyError.args; + notify.error({ + title: t['com.affine.minimum-client.title'](), + message: + t[ + `com.affine.minimum-client.${action === 'upgrade' ? 'outdated' : 'advanced'}.message` + ](), + action: { + label: + t[ + `com.affine.minimum-client.${action === 'upgrade' ? 'outdated' : 'advanced'}.button` + ](), + onClick: () => + window.open( + BUILD_CONFIG.downloadUrl, + '_blank', + 'noreferrer noopener' + ), + buttonProps: { + variant: 'primary', + }, + }, + }); + } else { + // TODO(@eyhn): better error handling + notify.error({ + title: 'Failed to send email. Please try again.', + }); + } } setIsMutating(false); - }, [authService, changeState, email]); + }, [authService, changeState, email, t]); const onAddSelfhosted = useCallback(() => { changeState(prev => ({ 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 3e4c007826116..8d16b276145ad 100644 --- a/packages/frontend/core/src/desktop/pages/auth/magic-link.tsx +++ b/packages/frontend/core/src/desktop/pages/auth/magic-link.tsx @@ -1,11 +1,9 @@ -import { ErrorNames, UserFriendlyError } from '@affine/graphql'; import { useService } from '@toeverything/infra'; import { useEffect, useRef } from 'react'; import { type LoaderFunction, redirect, useLoaderData, - // eslint-disable-next-line @typescript-eslint/no-restricted-imports useNavigate, } from 'react-router-dom'; @@ -79,16 +77,8 @@ export const Component = () => { } }); }) - .catch(err => { - const userFriendlyError = UserFriendlyError.fromAnyError(err); - if (userFriendlyError.name === ErrorNames.UNSUPPORTED_CLIENT_VERSION) { - const { action } = userFriendlyError.args; - nav( - `/sign-in?error=${encodeURIComponent(userFriendlyError.message)}&action=${encodeURIComponent(action as string)}` - ); - return; - } - nav(`/sign-in?error=${encodeURIComponent(err.message)}`); + .catch(e => { + nav(`/sign-in?error=${encodeURIComponent(e.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 31e734246fce9..a233e0bccc864 100644 --- a/packages/frontend/core/src/desktop/pages/auth/oauth-callback.tsx +++ b/packages/frontend/core/src/desktop/pages/auth/oauth-callback.tsx @@ -1,11 +1,9 @@ -import { ErrorNames, UserFriendlyError } from '@affine/graphql'; import { useService } from '@toeverything/infra'; import { useEffect, useRef } from 'react'; import { type LoaderFunction, redirect, useLoaderData, - // eslint-disable-next-line @typescript-eslint/no-restricted-imports useNavigate, } from 'react-router-dom'; @@ -81,17 +79,8 @@ export const Component = () => { // TODO(@forehalo): need a good way to go back to previous tab and close current one nav(redirectUri ?? '/'); }) - .catch(err => { - const userFriendlyError = UserFriendlyError.fromAnyError(err); - if (userFriendlyError.name === ErrorNames.UNSUPPORTED_CLIENT_VERSION) { - const { action } = userFriendlyError.args; - nav( - `/sign-in?error=${encodeURIComponent(userFriendlyError.message)}&action=${encodeURIComponent(action as string)}` - ); - return; - } - nav(`/sign-in?error=${encodeURIComponent(err.message)}`); - return; + .catch(e => { + nav(`/sign-in?error=${encodeURIComponent(e.message)}`); }); }, [data, auth, nav]); diff --git a/packages/frontend/core/src/modules/cloud/stores/auth.ts b/packages/frontend/core/src/modules/cloud/stores/auth.ts index b909e3885c0fe..89d0450dddf81 100644 --- a/packages/frontend/core/src/modules/cloud/stores/auth.ts +++ b/packages/frontend/core/src/modules/cloud/stores/auth.ts @@ -1,12 +1,15 @@ import { + ErrorNames, removeAvatarMutation, updateUserProfileMutation, uploadAvatarMutation, + UserFriendlyError, } from '@affine/graphql'; import { Store } from '@toeverything/infra'; import type { GlobalState } from '../../storage'; import type { AuthSessionInfo } from '../entities/session'; +import { BackendError } from '../error'; import type { FetchService } from '../services/fetch'; import type { GraphQLService } from '../services/graphql'; import type { ServerService } from '../services/server'; @@ -99,6 +102,12 @@ export class AuthStore extends Store { }); if (!res.ok) { + const error = await res.json(); + + const userFriendlyError = UserFriendlyError.fromAnyError(error); + if (userFriendlyError.name === ErrorNames.UNSUPPORTED_CLIENT_VERSION) { + throw new BackendError(userFriendlyError, res.status); + } throw new Error(`Failed to check user by email: ${email}`); } diff --git a/packages/frontend/core/src/modules/desktop-api/service/desktop-api.ts b/packages/frontend/core/src/modules/desktop-api/service/desktop-api.ts index 9f2beb7783193..1d00ca2273436 100644 --- a/packages/frontend/core/src/modules/desktop-api/service/desktop-api.ts +++ b/packages/frontend/core/src/modules/desktop-api/service/desktop-api.ts @@ -1,4 +1,5 @@ import { notify } from '@affine/component'; +import { ErrorNames, UserFriendlyError } from '@affine/graphql'; import { I18n } from '@affine/i18n'; import { init, @@ -173,10 +174,37 @@ export class DesktopApiService extends Service { } } })().catch(e => { - notify.error({ - title: I18n['com.affine.auth.toast.title.failed'](), - message: (e as any).message, - }); + const userFriendlyError = UserFriendlyError.fromAnyError(e); + if (userFriendlyError.name === ErrorNames.UNSUPPORTED_CLIENT_VERSION) { + const { action } = userFriendlyError.args; + notify.error({ + title: I18n['com.affine.minimum-client.title'](), + message: + I18n[ + `com.affine.minimum-client.${action === 'upgrade' ? 'outdated' : 'advanced'}.message` + ](), + action: { + label: + I18n[ + `com.affine.minimum-client.${action === 'upgrade' ? 'outdated' : 'advanced'}.button` + ](), + onClick: () => + window.open( + BUILD_CONFIG.downloadUrl, + '_blank', + 'noreferrer noopener' + ), + buttonProps: { + variant: 'primary', + }, + }, + }); + } else { + notify.error({ + title: I18n['com.affine.auth.toast.title.failed'](), + message: (e as any).message, + }); + } }); }); } diff --git a/packages/frontend/graphql/src/error.ts b/packages/frontend/graphql/src/error.ts index c10623454abd5..6ecde21b71318 100644 --- a/packages/frontend/graphql/src/error.ts +++ b/packages/frontend/graphql/src/error.ts @@ -8,7 +8,7 @@ export interface UserFriendlyErrorResponse { type: string; name: ErrorNames; message: string; - args?: any; + data?: any; stacktrace?: string; } @@ -21,7 +21,7 @@ export class UserFriendlyError readonly type = this.response.type; override readonly name = this.response.name; override readonly message = this.response.message; - readonly args = this.response.args; + readonly args = this.response.data; readonly stacktrace = this.response.stacktrace; static fromAnyError(response: any) {