fix(core): improve client-app navigation flow after team workspace upgrade (#11201)

This commit is contained in:
JimmFly
2025-03-27 03:23:29 +00:00
parent 5fbee7cc88
commit b00584c4cc
6 changed files with 91 additions and 66 deletions

View File

@@ -47,7 +47,8 @@ export const generateSubscriptionCallbackLink = (
account: AuthAccountInfo | null, account: AuthAccountInfo | null,
plan: SubscriptionPlan, plan: SubscriptionPlan,
recurring: SubscriptionRecurring, recurring: SubscriptionRecurring,
workspaceId?: string workspaceId?: string,
clientScheme?: string
) => { ) => {
const baseUrl = const baseUrl =
plan === SubscriptionPlan.AI plan === SubscriptionPlan.AI
@@ -79,7 +80,7 @@ export const generateSubscriptionCallbackLink = (
workspaceId ?? '', workspaceId ?? '',
].join(separator); ].join(separator);
return `${baseUrl}?info=${encodeURIComponent(query)}`; return `${baseUrl}?info=${encodeURIComponent(query)}${clientScheme ? `&client=${clientScheme}` : ''}`;
}; };
export const getSubscriptionInfo = (searchParams: URLSearchParams) => { export const getSubscriptionInfo = (searchParams: URLSearchParams) => {

View File

@@ -8,6 +8,7 @@ import {
SubscriptionService, SubscriptionService,
} from '@affine/core/modules/cloud'; } from '@affine/core/modules/cloud';
import { GlobalDialogService } from '@affine/core/modules/dialogs'; import { GlobalDialogService } from '@affine/core/modules/dialogs';
import { UrlService } from '@affine/core/modules/url';
import { import {
type CreateCheckoutSessionInput, type CreateCheckoutSessionInput,
SubscriptionPlan, SubscriptionPlan,
@@ -249,19 +250,25 @@ const Downgrade = ({ disabled }: { disabled?: boolean }) => {
const UpgradeToTeam = ({ recurring }: { recurring: SubscriptionRecurring }) => { const UpgradeToTeam = ({ recurring }: { recurring: SubscriptionRecurring }) => {
const t = useI18n(); const t = useI18n();
const serverService = useService(ServerService); const serverService = useService(ServerService);
const urlService = useService(UrlService);
const url = `${serverService.server.baseUrl}/upgrade-to-team?recurring=${recurring}`; const url = `${serverService.server.baseUrl}/upgrade-to-team?recurring=${recurring}`;
const scheme = urlService.getClientScheme();
const urlParams = new URLSearchParams();
if (scheme) {
urlParams.set('client', scheme);
}
return ( return (
<a <a
className={styles.planAction} className={styles.planAction}
href={url} href={`${url}${urlParams.toString() ? `&${urlParams.toString()}` : ''}`}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
> >
<Button <Button
className={styles.planAction} className={styles.planAction}
variant="primary" variant="primary"
data-event-args-url={url} data-event-args-url={`${url}${urlParams.toString() ? `&${urlParams.toString()}` : ''}`}
> >
{t['com.affine.payment.upgrade']()} {t['com.affine.payment.upgrade']()}
</Button> </Button>
@@ -289,6 +296,8 @@ export const Upgrade = ({
}) => { }) => {
const t = useI18n(); const t = useI18n();
const authService = useService(AuthService); const authService = useService(AuthService);
const urlService = useService(UrlService);
const schema = urlService.getClientScheme();
const handleBeforeCheckout = useCallback(() => { const handleBeforeCheckout = useCallback(() => {
track.$.settingsPanel.plans.checkout({ track.$.settingsPanel.plans.checkout({
@@ -308,7 +317,8 @@ export const Upgrade = ({
authService.session.account$.value, authService.session.account$.value,
plan, plan,
recurring, recurring,
workspaceId workspaceId || '',
schema
), ),
...checkoutInput, ...checkoutInput,
}), }),
@@ -317,6 +327,7 @@ export const Upgrade = ({
checkoutInput, checkoutInput,
plan, plan,
recurring, recurring,
schema,
workspaceId, workspaceId,
] ]
); );

View File

@@ -16,14 +16,15 @@ export const Component = () => {
const t = useI18n(); const t = useI18n();
const [params] = useSearchParams(); const [params] = useSearchParams();
const { jumpToIndex, jumpToOpenInApp } = useNavigateHelper(); const { jumpToOpenInApp } = useNavigateHelper();
const openAffine = useCallback(() => { const openAFFiNE = useCallback(() => {
if (params.get('scheme')) { if (params.get('client')) {
jumpToOpenInApp('bring-to-front'); return jumpToOpenInApp('bring-to-front');
} else { } else {
jumpToIndex(); // close popup window
return window.close();
} }
}, [jumpToIndex, jumpToOpenInApp, params]); }, [jumpToOpenInApp, params]);
const subtitle = ( const subtitle = (
<div className={styles.leftContentText}> <div className={styles.leftContentText}>
@@ -49,7 +50,7 @@ export const Component = () => {
title={t['com.affine.payment.ai-upgrade-success-page.title']()} title={t['com.affine.payment.ai-upgrade-success-page.title']()}
subtitle={subtitle} subtitle={subtitle}
> >
<Button variant="primary" size="extraLarge" onClick={openAffine}> <Button variant="primary" size="extraLarge" onClick={openAFFiNE}>
{t['com.affine.other-page.nav.open-affine']()} {t['com.affine.other-page.nav.open-affine']()}
</Button> </Button>
</AuthPageContainer> </AuthPageContainer>

View File

@@ -1,4 +1,5 @@
import { Button, Loading } from '@affine/component'; import { Button, Loading } from '@affine/component';
import { UrlService } from '@affine/core/modules/url';
import { UserFriendlyError } from '@affine/error'; import { UserFriendlyError } from '@affine/error';
import { import {
SubscriptionPlan, SubscriptionPlan,
@@ -95,9 +96,10 @@ function getProductTriple(searchParams: URLSearchParams): ProductTriple {
} }
export const Component = () => { export const Component = () => {
const { authService, subscriptionService } = useServices({ const { authService, subscriptionService, urlService } = useServices({
AuthService, AuthService,
SubscriptionService, SubscriptionService,
UrlService,
}); });
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const [message, setMessage] = useState(''); const [message, setMessage] = useState('');
@@ -155,7 +157,8 @@ export const Component = () => {
), ),
}); });
setMessage('Redirecting...'); setMessage('Redirecting...');
location.href = checkout; urlService.openPopupWindow(checkout);
jumpToIndex();
} catch (err) { } catch (err) {
const e = UserFriendlyError.fromAny(err); const e = UserFriendlyError.fromAny(err);
setMessage(e.message); setMessage(e.message);
@@ -180,6 +183,7 @@ export const Component = () => {
retryKey, retryKey,
variant, variant,
coupon, coupon,
urlService,
]); ]);
return ( return (

View File

@@ -16,16 +16,15 @@ export const Component = () => {
const t = useI18n(); const t = useI18n();
const [params] = useSearchParams(); const [params] = useSearchParams();
const { jumpToIndex, jumpToOpenInApp } = useNavigateHelper(); const { jumpToOpenInApp } = useNavigateHelper();
const openAffine = useCallback(() => { const openAFFiNE = useCallback(() => {
if (params.get('schema')) { if (params.get('client')) {
jumpToOpenInApp('bring-to-front'); return jumpToOpenInApp('bring-to-front');
} else { } else {
jumpToIndex(); // close popup window
return window.close();
} }
// close popup window }, [jumpToOpenInApp, params]);
window.close();
}, [jumpToIndex, jumpToOpenInApp, params]);
const subtitle = ( const subtitle = (
<div className={styles.leftContentText}> <div className={styles.leftContentText}>
@@ -51,7 +50,7 @@ export const Component = () => {
title={t['com.affine.payment.upgrade-success-page.title']()} title={t['com.affine.payment.upgrade-success-page.title']()}
subtitle={subtitle} subtitle={subtitle}
> >
<Button variant="primary" size="extraLarge" onClick={openAffine}> <Button variant="primary" size="extraLarge" onClick={openAFFiNE}>
{t['com.affine.other-page.nav.open-affine']()} {t['com.affine.other-page.nav.open-affine']()}
</Button> </Button>
</AuthPageContainer> </AuthPageContainer>

View File

@@ -71,9 +71,12 @@ export const UpgradeToTeam = ({ recurring }: { recurring: string | null }) => {
const [selectedWorkspace, setSelectedWorkspace] = const [selectedWorkspace, setSelectedWorkspace] =
useState<WorkspaceMetadata | null>(null); useState<WorkspaceMetadata | null>(null);
const information = useWorkspaceInfo(selectedWorkspace || undefined); const workspacesService = useService(WorkspacesService);
const profile = selectedWorkspace
const name = information?.name ?? UNTITLED_WORKSPACE_NAME; ? workspacesService.getProfile(selectedWorkspace)
: undefined;
const workspaceInfo = useLiveData(profile?.profile$);
const name = workspaceInfo?.name ?? UNTITLED_WORKSPACE_NAME;
const menuTriggerText = useMemo(() => { const menuTriggerText = useMemo(() => {
if (selectedWorkspace) { if (selectedWorkspace) {
@@ -92,6 +95,39 @@ export const UpgradeToTeam = ({ recurring }: { recurring: string | null }) => {
setOpenCreate(true); setOpenCreate(true);
}, []); }, []);
const revalidate = useCallback(() => {
profile?.revalidate();
}, [profile]);
const { jumpToPage, jumpToOpenInApp } = useNavigateHelper();
const [params] = useSearchParams();
const isTeam = workspaceInfo?.isTeam;
const openAFFiNE = useCallback(() => {
if (params.get('client')) {
jumpToOpenInApp(`/workspace/${selectedWorkspace?.id}/all`);
} else if (selectedWorkspace) {
jumpToPage(selectedWorkspace.id, 'all');
}
}, [jumpToOpenInApp, jumpToPage, params, selectedWorkspace]);
useEffect(() => {
revalidate();
}, [selectedWorkspace, revalidate]);
useEffect(() => {
window.addEventListener('focus', revalidate);
return () => {
window.removeEventListener('focus', revalidate);
};
}, [revalidate]);
useEffect(() => {
if (isTeam && selectedWorkspace) {
return openAFFiNE();
}
}, [isTeam, jumpToPage, openAFFiNE, selectedWorkspace]);
return ( return (
<AuthPageContainer title={t['com.affine.upgrade-to-team-page.title']()}> <AuthPageContainer title={t['com.affine.upgrade-to-team-page.title']()}>
<div className={styles.root}> <div className={styles.root}>
@@ -139,12 +175,15 @@ export const UpgradeToTeam = ({ recurring }: { recurring: string | null }) => {
<div> <div>
{t['com.affine.upgrade-to-team-page.benefit.description']()} {t['com.affine.upgrade-to-team-page.benefit.description']()}
</div> </div>
<UpgradeDialog {selectedWorkspace && (
recurring={recurring} <UpgradeDialog
open={openUpgrade} recurring={recurring}
onOpenChange={setOpenUpgrade} open={openUpgrade}
selectedWorkspace={selectedWorkspace} onOpenChange={setOpenUpgrade}
/> workspaceId={selectedWorkspace.id}
workspaceName={name}
/>
)}
<CreateWorkspaceDialog <CreateWorkspaceDialog
open={openCreate} open={openCreate}
onOpenChange={setOpenCreate} onOpenChange={setOpenCreate}
@@ -172,52 +211,22 @@ export const UpgradeToTeam = ({ recurring }: { recurring: string | null }) => {
const UpgradeDialog = ({ const UpgradeDialog = ({
open, open,
onOpenChange, onOpenChange,
selectedWorkspace, workspaceId,
workspaceName,
recurring, recurring,
}: { }: {
open: boolean; open: boolean;
selectedWorkspace: WorkspaceMetadata | null; workspaceId: string;
workspaceName: string;
recurring: string | null; recurring: string | null;
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
}) => { }) => {
const t = useI18n(); const t = useI18n();
const workspacesService = useService(WorkspacesService);
const { jumpToPage } = useNavigateHelper();
const profile = selectedWorkspace
? workspacesService.getProfile(selectedWorkspace)
: undefined;
const workspaceInfo = useLiveData(profile?.profile$);
const isTeam = workspaceInfo?.isTeam;
const workspaceName = workspaceInfo?.name;
const workspaceId = selectedWorkspace?.id;
const onClose = useCallback(() => { const onClose = useCallback(() => {
onOpenChange(false); onOpenChange(false);
}, [onOpenChange]); }, [onOpenChange]);
const revalidate = useCallback(() => {
profile?.revalidate();
}, [profile]);
useEffect(() => {
revalidate();
}, [selectedWorkspace, revalidate]);
useEffect(() => {
window.addEventListener('focus', revalidate);
return () => {
window.removeEventListener('focus', revalidate);
};
}, [revalidate]);
useEffect(() => {
if (isTeam && selectedWorkspace) {
onClose();
return jumpToPage(selectedWorkspace.id, 'all');
}
}, [isTeam, jumpToPage, onClose, selectedWorkspace]);
const currentRecurring = const currentRecurring =
recurring && recurring &&
recurring.toLowerCase() === SubscriptionRecurring.Yearly.toLowerCase() recurring.toLowerCase() === SubscriptionRecurring.Yearly.toLowerCase()