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

View File

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

View File

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

View File

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

View File

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

View File

@@ -71,9 +71,12 @@ export const UpgradeToTeam = ({ recurring }: { recurring: string | null }) => {
const [selectedWorkspace, setSelectedWorkspace] =
useState<WorkspaceMetadata | null>(null);
const information = useWorkspaceInfo(selectedWorkspace || undefined);
const name = information?.name ?? UNTITLED_WORKSPACE_NAME;
const workspacesService = useService(WorkspacesService);
const profile = selectedWorkspace
? workspacesService.getProfile(selectedWorkspace)
: undefined;
const workspaceInfo = useLiveData(profile?.profile$);
const name = workspaceInfo?.name ?? UNTITLED_WORKSPACE_NAME;
const menuTriggerText = useMemo(() => {
if (selectedWorkspace) {
@@ -92,6 +95,39 @@ export const UpgradeToTeam = ({ recurring }: { recurring: string | null }) => {
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 (
<AuthPageContainer title={t['com.affine.upgrade-to-team-page.title']()}>
<div className={styles.root}>
@@ -139,12 +175,15 @@ export const UpgradeToTeam = ({ recurring }: { recurring: string | null }) => {
<div>
{t['com.affine.upgrade-to-team-page.benefit.description']()}
</div>
<UpgradeDialog
recurring={recurring}
open={openUpgrade}
onOpenChange={setOpenUpgrade}
selectedWorkspace={selectedWorkspace}
/>
{selectedWorkspace && (
<UpgradeDialog
recurring={recurring}
open={openUpgrade}
onOpenChange={setOpenUpgrade}
workspaceId={selectedWorkspace.id}
workspaceName={name}
/>
)}
<CreateWorkspaceDialog
open={openCreate}
onOpenChange={setOpenCreate}
@@ -172,52 +211,22 @@ export const UpgradeToTeam = ({ recurring }: { recurring: string | null }) => {
const UpgradeDialog = ({
open,
onOpenChange,
selectedWorkspace,
workspaceId,
workspaceName,
recurring,
}: {
open: boolean;
selectedWorkspace: WorkspaceMetadata | null;
workspaceId: string;
workspaceName: string;
recurring: string | null;
onOpenChange: (open: boolean) => void;
}) => {
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(() => {
onOpenChange(false);
}, [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 =
recurring &&
recurring.toLowerCase() === SubscriptionRecurring.Yearly.toLowerCase()