From 12a2ccf1a5ae381ca76bfff13a59a0bec94344fd Mon Sep 17 00:00:00 2001 From: Joooye_34 Date: Thu, 9 Nov 2023 15:38:52 +0800 Subject: [PATCH] fix(core): visit /signin pay when already logged and subscribed (#4882) Co-authored-by: Peng Xiao --- .../affine/auth/subscription-redirect.css.ts | 21 +++++ .../affine/auth/subscription-redirect.tsx | 80 ++++++++++++++++--- packages/frontend/core/src/pages/sign-in.tsx | 73 ++++++++++++----- packages/frontend/i18n/src/resources/en.json | 1 + 4 files changed, 143 insertions(+), 32 deletions(-) diff --git a/packages/frontend/core/src/components/affine/auth/subscription-redirect.css.ts b/packages/frontend/core/src/components/affine/auth/subscription-redirect.css.ts index d0ba8345c5..c72392b20f 100644 --- a/packages/frontend/core/src/components/affine/auth/subscription-redirect.css.ts +++ b/packages/frontend/core/src/components/affine/auth/subscription-redirect.css.ts @@ -7,3 +7,24 @@ export const loadingContainer = style({ justifyContent: 'center', alignItems: 'center', }); + +export const subscriptionLayout = style({ + margin: '10% auto', + maxWidth: '536px', +}); + +export const subscriptionBox = style({ + padding: '48px 52px', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', +}); + +export const subscriptionTips = style({ + margin: '20px 0', + color: 'var(--affine-text-secondary-color)', + fontSize: '12px', + fontStyle: 'normal', + fontWeight: '400', + lineHeight: '20px', +}); diff --git a/packages/frontend/core/src/components/affine/auth/subscription-redirect.tsx b/packages/frontend/core/src/components/affine/auth/subscription-redirect.tsx index 85f96c1e3a..e142148eea 100644 --- a/packages/frontend/core/src/components/affine/auth/subscription-redirect.tsx +++ b/packages/frontend/core/src/components/affine/auth/subscription-redirect.tsx @@ -1,16 +1,62 @@ +import { AffineShapeIcon } from '@affine/component/page-list'; import type { SubscriptionRecurring } from '@affine/graphql'; -import { checkoutMutation } from '@affine/graphql'; -import { useMutation } from '@affine/workspace/affine/gql'; +import { checkoutMutation, subscriptionQuery } from '@affine/graphql'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { useMutation, useQuery } from '@affine/workspace/affine/gql'; +import { Button } from '@toeverything/components/button'; import { Loading } from '@toeverything/components/loading'; import { nanoid } from 'nanoid'; -import { type FC, useEffect, useMemo } from 'react'; +import { type FC, Suspense, useCallback, useEffect, useMemo } from 'react'; +import { + RouteLogic, + useNavigateHelper, +} from '../../../hooks/use-navigate-helper'; import * as styles from './subscription-redirect.css'; import { useSubscriptionSearch } from './use-subscription'; -export const SubscriptionRedirect: FC = () => { +const CenterLoading = () => { + return ( +
+ +
+ ); +}; + +const SubscriptionExisting = () => { + const t = useAFFiNEI18N(); + const { jumpToIndex } = useNavigateHelper(); + + const onButtonClick = useCallback(() => { + jumpToIndex(RouteLogic.REPLACE); + }, [jumpToIndex]); + + return ( +
+
+ +

+ {t['com.affine.payment.subscription.exist']()} +

+ +
+
+ ); +}; + +const SubscriptionRedirectInner: FC = () => { const subscriptionData = useSubscriptionSearch(); const idempotencyKey = useMemo(() => nanoid(), []); + const { data } = useQuery({ + query: subscriptionQuery, + }); const { trigger: checkoutSubscription } = useMutation({ mutation: checkoutMutation, }); @@ -20,12 +66,18 @@ export const SubscriptionRedirect: FC = () => { throw new Error('No subscription data found'); } + if (data.currentUser?.subscription) { + return; + } + // This component will be render multiple times, use timeout to avoid multiple effect. const timeoutId = setTimeout(() => { const recurring = subscriptionData.recurring as SubscriptionRecurring; - checkoutSubscription({ recurring, idempotencyKey }).then(data => { - window.open(data.checkout, '_self', 'norefferer'); - }); + checkoutSubscription({ recurring, idempotencyKey }).then( + ({ checkout }) => { + window.open(checkout, '_self', 'norefferer'); + } + ); }, 100); return () => { @@ -36,9 +88,17 @@ export const SubscriptionRedirect: FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + if (data.currentUser?.subscription) { + return ; + } + + return ; +}; + +export const SubscriptionRedirect = () => { return ( -
- -
+ }> + + ); }; diff --git a/packages/frontend/core/src/pages/sign-in.tsx b/packages/frontend/core/src/pages/sign-in.tsx index b759891822..68f735c6a4 100644 --- a/packages/frontend/core/src/pages/sign-in.tsx +++ b/packages/frontend/core/src/pages/sign-in.tsx @@ -1,11 +1,13 @@ import { SignInPageContainer } from '@affine/component/auth-components'; import { useAtom } from 'jotai'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useRef } from 'react'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { useLocation, useNavigate } from 'react-router-dom'; import { authAtom } from '../atoms'; -import { AuthPanel } from '../components/affine/auth'; +import { AuthPanel, type AuthProps } from '../components/affine/auth'; +import { SubscriptionRedirect } from '../components/affine/auth/subscription-redirect'; +import { useSubscriptionSearch } from '../components/affine/auth/use-subscription'; import { useCurrentLoginStatus } from '../hooks/affine/use-current-login-status'; import { RouteLogic, useNavigateHelper } from '../hooks/use-navigate-helper'; @@ -15,15 +17,31 @@ interface LocationState { }; } export const Component = () => { + const paymentRedirectRef = useRef<'redirect' | 'ignore' | null>(null); const [{ state, email = '', emailType = 'changePassword' }, setAuthAtom] = useAtom(authAtom); const loginStatus = useCurrentLoginStatus(); const location = useLocation() as LocationState; const navigate = useNavigate(); const { jumpToIndex } = useNavigateHelper(); + const subscriptionData = useSubscriptionSearch(); + + const isLoggedIn = loginStatus === 'authenticated'; + + // Check payment redirect once after session loaded, to avoid unnecessary page rendering. + if (loginStatus !== 'loading' && !paymentRedirectRef.current) { + // If user is logged in and visit sign in page with subscription query, redirect to stripe payment page immediately. + // Otherwise, user will login through email, and then redirect to payment page. + paymentRedirectRef.current = + subscriptionData && isLoggedIn ? 'redirect' : 'ignore'; + } useEffect(() => { - if (loginStatus === 'authenticated') { + if (paymentRedirectRef.current === 'redirect') { + return; + } + + if (isLoggedIn) { if (location.state?.callbackURL) { navigate(location.state.callbackURL, { replace: true, @@ -35,35 +53,46 @@ export const Component = () => { }, [ jumpToIndex, location.state?.callbackURL, - loginStatus, navigate, setAuthAtom, + subscriptionData, + isLoggedIn, ]); + const onSetEmailType = useCallback( + (emailType: AuthProps['emailType']) => { + setAuthAtom(prev => ({ ...prev, emailType })); + }, + [setAuthAtom] + ); + + const onSetAuthState = useCallback( + (state: AuthProps['state']) => { + setAuthAtom(prev => ({ ...prev, state })); + }, + [setAuthAtom] + ); + + const onSetAuthEmail = useCallback( + (email: AuthProps['email']) => { + setAuthAtom(prev => ({ ...prev, email })); + }, + [setAuthAtom] + ); + + if (paymentRedirectRef.current === 'redirect') { + return ; + } + return ( { - setAuthAtom(prev => ({ ...prev, emailType })); - }, - [setAuthAtom] - )} - setAuthState={useCallback( - state => { - setAuthAtom(prev => ({ ...prev, state })); - }, - [setAuthAtom] - )} - setAuthEmail={useCallback( - email => { - setAuthAtom(prev => ({ ...prev, email })); - }, - [setAuthAtom] - )} + setEmailType={onSetEmailType} + setAuthState={onSetAuthState} + setAuthEmail={onSetAuthEmail} /> ); diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index 03cb2153a9..07cca76d48 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -766,6 +766,7 @@ "com.affine.payment.upgrade-success-page.title": "Upgrade Successful!", "com.affine.payment.upgrade-success-page.text": "Congratulations! Your AFFiNE account has been successfully upgraded to a Pro account.", "com.affine.payment.upgrade-success-page.support": "If you have any questions, please contact our <1> customer support.", + "com.affine.payment.subscription.exist": "You already have a subscription.", "com.affine.other-page.nav.official-website": "Official Website", "com.affine.other-page.nav.affine-community": "AFFiNE Community", "com.affine.other-page.nav.blog": "Blog",