mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-17 06:16:59 +08:00
feat(core): check user's subscription at ai onboarding stage (#6608)
This commit is contained in:
@@ -47,15 +47,19 @@ export const video = style({
|
|||||||
height: 'calc(100% + 4px)',
|
height: 'calc(100% + 4px)',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const mainContent = style({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 4,
|
||||||
|
padding: '20px 24px 0px 24px',
|
||||||
|
});
|
||||||
export const title = style({
|
export const title = style({
|
||||||
padding: '20px 24px 8px 24px',
|
|
||||||
fontSize: cssVar('fontH6'),
|
fontSize: cssVar('fontH6'),
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
lineHeight: '26px',
|
lineHeight: '26px',
|
||||||
color: cssVar('textPrimaryColor'),
|
color: cssVar('textPrimaryColor'),
|
||||||
});
|
});
|
||||||
export const description = style({
|
export const description = style({
|
||||||
padding: '0px 24px',
|
|
||||||
fontSize: cssVar('fontBase'),
|
fontSize: cssVar('fontBase'),
|
||||||
lineHeight: '24px',
|
lineHeight: '24px',
|
||||||
minHeight: 48,
|
minHeight: 48,
|
||||||
@@ -66,14 +70,45 @@ export const link = style({
|
|||||||
color: cssVar('textEmphasisColor'),
|
color: cssVar('textEmphasisColor'),
|
||||||
textDecoration: 'underline',
|
textDecoration: 'underline',
|
||||||
});
|
});
|
||||||
|
export const privacy = style({
|
||||||
|
padding: '20px 24px 0px 24px',
|
||||||
|
color: cssVar('textSecondaryColor'),
|
||||||
|
fontSize: cssVar('fontXs'),
|
||||||
|
fontWeight: 400,
|
||||||
|
lineHeight: '20px',
|
||||||
|
height: 44,
|
||||||
|
transition: 'all 0.3s',
|
||||||
|
overflow: 'hidden',
|
||||||
|
|
||||||
|
selectors: {
|
||||||
|
'&[aria-hidden="true"]': {
|
||||||
|
paddingTop: 0,
|
||||||
|
height: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export const privacyLink = style({
|
||||||
|
color: 'inherit',
|
||||||
|
textDecoration: 'underline',
|
||||||
|
});
|
||||||
|
|
||||||
export const footer = style({
|
export const footer = style({
|
||||||
|
width: '100%',
|
||||||
padding: '20px 28px',
|
padding: '20px 28px',
|
||||||
gap: 12,
|
gap: 12,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'space-between',
|
||||||
|
selectors: {
|
||||||
|
'&[data-is-last="true"], &[data-is-first="true"]': {
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
export const baseActionButton = style({
|
||||||
export const skipButton = style({
|
fontSize: cssVar('fontBase'),
|
||||||
fontWeight: 500,
|
selectors: {
|
||||||
|
'&.large': {
|
||||||
|
fontWeight: 500,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { Button, Modal } from '@affine/component';
|
import { Button, Modal } from '@affine/component';
|
||||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||||
import { useBlurRoot } from '@affine/core/hooks/use-blur-root';
|
import { useBlurRoot } from '@affine/core/hooks/use-blur-root';
|
||||||
|
import { SubscriptionService } from '@affine/core/modules/cloud';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import { Trans } from '@affine/i18n';
|
import { Trans } from '@affine/i18n';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
import { ArrowLeftSmallIcon } from '@blocksuite/icons';
|
||||||
import {
|
import {
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useServices,
|
useServices,
|
||||||
@@ -68,7 +70,10 @@ const getPlayList = (t: Translate): Array<PlayListItem> => [
|
|||||||
export const AIOnboardingGeneral = ({
|
export const AIOnboardingGeneral = ({
|
||||||
onDismiss,
|
onDismiss,
|
||||||
}: BaseAIOnboardingDialogProps) => {
|
}: BaseAIOnboardingDialogProps) => {
|
||||||
const { workspaceService } = useServices({ WorkspaceService });
|
const { workspaceService, subscriptionService } = useServices({
|
||||||
|
WorkspaceService,
|
||||||
|
SubscriptionService,
|
||||||
|
});
|
||||||
|
|
||||||
const videoWrapperRef = useRef<HTMLDivElement | null>(null);
|
const videoWrapperRef = useRef<HTMLDivElement | null>(null);
|
||||||
const prevVideoRef = useRef<HTMLVideoElement | null>(null);
|
const prevVideoRef = useRef<HTMLVideoElement | null>(null);
|
||||||
@@ -76,6 +81,7 @@ export const AIOnboardingGeneral = ({
|
|||||||
workspaceService.workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD;
|
workspaceService.workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD;
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const open = useLiveData(showAIOnboardingGeneral$);
|
const open = useLiveData(showAIOnboardingGeneral$);
|
||||||
|
const aiSubscription = useLiveData(subscriptionService.subscription.ai$);
|
||||||
const [index, setIndex] = useState(0);
|
const [index, setIndex] = useState(0);
|
||||||
const list = useMemo(() => getPlayList(t), [t]);
|
const list = useMemo(() => getPlayList(t), [t]);
|
||||||
const setSettingModal = useSetAtom(openSettingModalAtom);
|
const setSettingModal = useSetAtom(openSettingModalAtom);
|
||||||
@@ -96,7 +102,6 @@ export const AIOnboardingGeneral = ({
|
|||||||
});
|
});
|
||||||
closeAndDismiss();
|
closeAndDismiss();
|
||||||
}, [closeAndDismiss, setSettingModal]);
|
}, [closeAndDismiss, setSettingModal]);
|
||||||
const onClose = useCallback(() => showAIOnboardingGeneral$.next(false), []);
|
|
||||||
const onPrev = useCallback(() => {
|
const onPrev = useCallback(() => {
|
||||||
setIndex(i => Math.max(0, i - 1));
|
setIndex(i => Math.max(0, i - 1));
|
||||||
}, []);
|
}, []);
|
||||||
@@ -104,6 +109,10 @@ export const AIOnboardingGeneral = ({
|
|||||||
setIndex(i => Math.min(list.length - 1, i + 1));
|
setIndex(i => Math.min(list.length - 1, i + 1));
|
||||||
}, [list.length]);
|
}, [list.length]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
subscriptionService.subscription.revalidate();
|
||||||
|
}, [subscriptionService]);
|
||||||
|
|
||||||
const videoRenderer = useCallback(
|
const videoRenderer = useCallback(
|
||||||
({ video }: PlayListItem, index: number) => (
|
({ video }: PlayListItem, index: number) => (
|
||||||
<div className={styles.videoWrapper}>
|
<div className={styles.videoWrapper}>
|
||||||
@@ -152,7 +161,10 @@ export const AIOnboardingGeneral = ({
|
|||||||
return isCloud ? (
|
return isCloud ? (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
onOpenChange={v => showAIOnboardingGeneral$.next(v)}
|
onOpenChange={v => {
|
||||||
|
showAIOnboardingGeneral$.next(v);
|
||||||
|
if (!v && isLast) onDismiss();
|
||||||
|
}}
|
||||||
contentOptions={{ className: styles.dialog }}
|
contentOptions={{ className: styles.dialog }}
|
||||||
overlayOptions={{ className: baseStyles.dialogOverlay }}
|
overlayOptions={{ className: baseStyles.dialogOverlay }}
|
||||||
>
|
>
|
||||||
@@ -166,7 +178,7 @@ export const AIOnboardingGeneral = ({
|
|||||||
itemRenderer={videoRenderer}
|
itemRenderer={videoRenderer}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<main>
|
<main className={styles.mainContent}>
|
||||||
<Slider<PlayListItem>
|
<Slider<PlayListItem>
|
||||||
items={list}
|
items={list}
|
||||||
activeIndex={index}
|
activeIndex={index}
|
||||||
@@ -181,28 +193,76 @@ export const AIOnboardingGeneral = ({
|
|||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer className={styles.footer}>
|
<section
|
||||||
|
className={styles.privacy}
|
||||||
|
aria-hidden={!isLast || !!aiSubscription}
|
||||||
|
>
|
||||||
|
<Trans
|
||||||
|
i18nKey="com.affine.ai-onboarding.general.privacy"
|
||||||
|
components={{
|
||||||
|
a: (
|
||||||
|
<a
|
||||||
|
className={styles.privacyLink}
|
||||||
|
href="https://ai.affine.pro"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer
|
||||||
|
className={styles.footer}
|
||||||
|
data-is-last={isLast}
|
||||||
|
data-is-first={isFirst}
|
||||||
|
>
|
||||||
{isLast ? (
|
{isLast ? (
|
||||||
<>
|
aiSubscription ? (
|
||||||
<Button onClick={closeAndDismiss}>
|
<Button
|
||||||
{t['com.affine.ai-onboarding.general.try-for-free']()}
|
className={styles.baseActionButton}
|
||||||
|
size="large"
|
||||||
|
onClick={closeAndDismiss}
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
{t['com.affine.ai-onboarding.general.get-started']()}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={goToPricingPlans} type="primary">
|
) : (
|
||||||
{t['com.affine.ai-onboarding.general.purchase']()}
|
<>
|
||||||
</Button>
|
<Button
|
||||||
</>
|
className={styles.baseActionButton}
|
||||||
|
size="large"
|
||||||
|
onClick={closeAndDismiss}
|
||||||
|
>
|
||||||
|
{t['com.affine.ai-onboarding.general.try-for-free']()}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={styles.baseActionButton}
|
||||||
|
size="large"
|
||||||
|
onClick={goToPricingPlans}
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
{t['com.affine.ai-onboarding.general.purchase']()}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{isFirst ? (
|
{isFirst ? null : (
|
||||||
<Button onClick={onClose} className={styles.skipButton}>
|
<Button
|
||||||
{t['com.affine.ai-onboarding.general.skip']()}
|
icon={<ArrowLeftSmallIcon />}
|
||||||
</Button>
|
className={styles.baseActionButton}
|
||||||
) : (
|
onClick={onPrev}
|
||||||
<Button onClick={onPrev}>
|
type="plain"
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
{t['com.affine.ai-onboarding.general.prev']()}
|
{t['com.affine.ai-onboarding.general.prev']()}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button type="primary" onClick={onNext}>
|
<Button
|
||||||
|
className={styles.baseActionButton}
|
||||||
|
size="large"
|
||||||
|
type="primary"
|
||||||
|
onClick={onNext}
|
||||||
|
>
|
||||||
{t['com.affine.ai-onboarding.general.next']()}
|
{t['com.affine.ai-onboarding.general.next']()}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1297,6 +1297,12 @@
|
|||||||
"com.affine.ai-onboarding.general.prev": "Back",
|
"com.affine.ai-onboarding.general.prev": "Back",
|
||||||
"com.affine.ai-onboarding.general.try-for-free": "Try for Free",
|
"com.affine.ai-onboarding.general.try-for-free": "Try for Free",
|
||||||
"com.affine.ai-onboarding.general.purchase": "Get Unlimited Usage",
|
"com.affine.ai-onboarding.general.purchase": "Get Unlimited Usage",
|
||||||
|
"com.affine.ai-onboarding.general.privacy": "By continuing, you should agree our <a>Terms of Services</a>.",
|
||||||
|
"com.affine.ai-onboarding.general.get-started": "Get Started",
|
||||||
|
"com.affine.ai-onboarding.local.title": "Meet AFFiNE AI",
|
||||||
|
"com.affine.ai-onboarding.local.message": "Lets you think bigger, create faster, work smarter and save time for every project.",
|
||||||
|
"com.affine.ai-onboarding.local.action-dismiss": "Dismiss",
|
||||||
|
"com.affine.ai-onboarding.local.action-learn-more": "Learn More",
|
||||||
"com.affine.ai-onboarding.edgeless.title": "Meet AFFiNE AI",
|
"com.affine.ai-onboarding.edgeless.title": "Meet AFFiNE AI",
|
||||||
"com.affine.ai-onboarding.edgeless.message": "Lets you think bigger, create faster, work smarter and save time for every project."
|
"com.affine.ai-onboarding.edgeless.message": "Lets you think bigger, create faster, work smarter and save time for every project."
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user