Skip to content

Commit a1ece72

Browse files
committed
feat: add vercel analysis track
1 parent 8a6aaf9 commit a1ece72

File tree

5 files changed

+259
-26
lines changed

5 files changed

+259
-26
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import noop from '../../utils/noop'
2+
import { hasSeenEnhancedPromotionAtom } from '@/store'
3+
import { Dialog, Transition } from '@headlessui/react'
4+
import { track } from '@vercel/analytics'
5+
import { useAtom } from 'jotai'
6+
import type React from 'react'
7+
import { Fragment, useEffect, useState } from 'react'
8+
import IconStar from '~icons/heroicons/star-solid'
9+
import IconX from '~icons/tabler/x'
10+
11+
const EnhancedPromotionModal: React.FC = () => {
12+
const [hasSeenPromotion, setHasSeenPromotion] = useAtom(hasSeenEnhancedPromotionAtom)
13+
const [isOpen, setIsOpen] = useState(false)
14+
15+
useEffect(() => {
16+
// Only show modal if user hasn't seen it before
17+
if (!hasSeenPromotion) {
18+
const timer = setTimeout(() => {
19+
setIsOpen(true)
20+
}, 2000) // Show after 2 seconds to let the page load
21+
22+
return () => clearTimeout(timer)
23+
}
24+
}, [hasSeenPromotion])
25+
26+
const handleTryNow = () => {
27+
track('promotion_event', {
28+
from: 'enhanced_promotion_modal',
29+
action: 'open',
30+
})
31+
setHasSeenPromotion(true)
32+
// setIsOpen(false)
33+
// Open in new tab
34+
window.open('https://qwertylearner.ai', '_blank')
35+
}
36+
37+
const handleDismiss = () => {
38+
track('promotion_event', {
39+
from: 'enhanced_promotion_modal',
40+
action: 'close',
41+
})
42+
setHasSeenPromotion(true)
43+
setIsOpen(false)
44+
}
45+
46+
return (
47+
<Transition.Root show={isOpen} as={Fragment}>
48+
<Dialog as="div" className="relative z-50" onClose={noop}>
49+
<Transition.Child
50+
as={Fragment}
51+
enter="ease-out duration-300"
52+
enterFrom="opacity-0"
53+
enterTo="opacity-100"
54+
leave="ease-in duration-200"
55+
leaveFrom="opacity-100"
56+
leaveTo="opacity-0"
57+
>
58+
<div className="fixed inset-0 bg-black bg-opacity-40 backdrop-blur-sm transition-opacity" />
59+
</Transition.Child>
60+
61+
<div className="fixed inset-0 z-10 overflow-y-auto">
62+
<div className="flex min-h-full items-center justify-center p-4 text-center">
63+
<Transition.Child
64+
as={Fragment}
65+
enter="ease-out duration-300"
66+
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
67+
enterTo="opacity-100 translate-y-0 sm:scale-100"
68+
leave="ease-in duration-200"
69+
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
70+
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
71+
>
72+
<Dialog.Panel className="dark:via-gray-850 relative transform overflow-hidden rounded-2xl border border-blue-200 bg-gradient-to-br from-white via-blue-50 to-indigo-100 p-0 text-left shadow-2xl transition-all dark:border-gray-700 dark:from-gray-800 dark:to-gray-900 sm:my-8 sm:w-full sm:max-w-xl">
73+
{/* Header with close button */}
74+
<div className="absolute right-4 top-4 z-10">
75+
<button
76+
type="button"
77+
onClick={handleDismiss}
78+
className="rounded-full p-1 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-gray-700 dark:hover:text-gray-300"
79+
title="关闭"
80+
>
81+
<IconX className="h-5 w-5" />
82+
</button>
83+
</div>
84+
85+
{/* Content */}
86+
<div className="px-8 pb-8 pt-10">
87+
{/* Icon and title */}
88+
<div className="mb-6 text-center">
89+
<div className="mx-auto mb-4 flex h-20 w-20 animate-pulse items-center justify-center rounded-full bg-gradient-to-r from-blue-500 to-purple-600 shadow-lg">
90+
<IconStar className="h-10 w-10 text-white" />
91+
</div>
92+
<h3 className="bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-3xl font-bold text-transparent">
93+
体验 QwertyLearner.ai
94+
</h3>
95+
96+
<p className="mt-2 text-sm text-gray-600 dark:text-gray-400">解锁更强大的学习体验 ✨</p>
97+
</div>
98+
99+
{/* Main content */}
100+
<div className="space-y-4 py-2 text-sm text-gray-700 dark:text-gray-300">
101+
<p className="text-center font-medium text-gray-900 dark:text-white">
102+
不会编程?想拥有自己的专属学习词典?操作简单,一键上传,点击即用
103+
<br />
104+
<div className="my-2"></div>
105+
那么,推荐您尝试由英国 DeepLearningAI 专业团队开发运营的 QwertyLearner.ai
106+
</p>
107+
108+
<div className="rounded-lg bg-white p-4 shadow-sm dark:bg-gray-800">
109+
<h4 className="mb-3 font-semibold text-gray-900 dark:text-white">🚀 专业功能</h4>
110+
<ul className="space-y-2.5">
111+
<li className="flex items-start">
112+
<span className="mr-2 mt-0.5 text-blue-500"></span>
113+
<span>
114+
<strong>AI 智能词库</strong> - 一键上传,智能生成释义和词性,打造专属自定义词库
115+
</span>
116+
</li>
117+
<li className="flex items-start">
118+
<span className="mr-2 mt-0.5 text-blue-500"></span>
119+
<span>
120+
<strong>文章练习</strong> - 自定义文章内容,提升实战能力
121+
</span>
122+
</li>
123+
<li className="flex items-start">
124+
<span className="mr-2 mt-0.5 text-blue-500"></span>
125+
<span>
126+
<strong>云端同步</strong> - 多设备练习记录、错题库同步
127+
</span>
128+
</li>
129+
<li className="flex items-start">
130+
<span className="mr-2 mt-0.5 text-blue-500"></span>
131+
<span>
132+
<strong>词典选择</strong> - 更多丰富的专业词库
133+
</span>
134+
</li>
135+
</ul>
136+
</div>
137+
138+
<div className="rounded-lg bg-amber-50 p-3 text-xs text-amber-800 dark:bg-amber-900/20 dark:text-amber-200">
139+
<p>
140+
<strong>说明:</strong>QwertyLearner.ai 由英国 DeepLearningAI 独立开发运营,为开源版 QwertyLearner
141+
的独立衍生版本,开源版将持续维持开源与开放运营。
142+
</p>
143+
</div>
144+
</div>
145+
146+
{/* Action buttons */}
147+
<div className="mt-8 space-y-3">
148+
<button
149+
type="button"
150+
onClick={handleTryNow}
151+
className="w-full transform rounded-lg bg-gradient-to-r from-blue-500 to-purple-600 px-8 py-4 text-lg font-bold text-white shadow-lg transition-all duration-200 hover:scale-105 hover:from-blue-600 hover:to-purple-700 hover:shadow-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
152+
>
153+
🚀 立即体验 QwertyLearner.ai
154+
</button>
155+
</div>
156+
</div>
157+
</Dialog.Panel>
158+
</Transition.Child>
159+
</div>
160+
</div>
161+
</Dialog>
162+
</Transition.Root>
163+
)
164+
}
165+
166+
export default EnhancedPromotionModal

src/pages/Gallery-N/DictRequest.tsx

Lines changed: 87 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import InfoPanel from '@/components/InfoPanel'
2+
import { track } from '@vercel/analytics'
23
import { useCallback, useState } from 'react'
34
import IconBook2 from '~icons/tabler/book-2'
45

@@ -7,47 +8,110 @@ export default function DictRequest() {
78

89
const onOpenPanel = useCallback(() => {
910
setShowPanel(true)
11+
track('promotion_event', {
12+
from: 'dict_request_button',
13+
action: 'open',
14+
})
1015
}, [])
1116

1217
const onClosePanel = useCallback(() => {
1318
setShowPanel(false)
19+
track('promotion_event', {
20+
from: 'dict_request_panel',
21+
action: 'close',
22+
})
1423
}, [])
1524

1625
return (
1726
<>
1827
{showPanel && (
1928
<InfoPanel
2029
openState={showPanel}
21-
title="申请词典"
30+
title="寻找更多词典?"
2231
icon={IconBook2}
2332
buttonClassName="bg-indigo-500 hover:bg-indigo-400"
2433
iconClassName="text-indigo-500 bg-indigo-100 dark:text-indigo-300 dark:bg-indigo-500"
2534
onClose={onClosePanel}
2635
>
27-
<p className="text-sm text-gray-600 dark:text-gray-300">
28-
如果您有相关的编程基础,可以参考
29-
<a
30-
href="https://github.com/Kaiyiwing/qwerty-learner/blob/master/docs/toBuildDict.md"
31-
className="px-2 text-blue-500"
32-
target="_blank"
33-
rel="noreferrer"
34-
>
35-
导入词典
36-
</a>
37-
,给项目贡献新的词典。
38-
<br />
39-
<br />
40-
如果您没有相关的编程基础,可以将您的字典需求发送邮件到{' '}
41-
<a href="mailto:me@kaiyi.cool" className="px-2 text-blue-500" aria-label="发送邮件到 me@kaiyi.cool">
42-
me@kaiyi.cool
43-
</a>
44-
,或者在网页底部添加我们的用户社群进行反馈。
45-
</p>
46-
<br />
36+
<div className="space-y-5">
37+
<div className="rounded-lg bg-white p-4 shadow-sm dark:bg-gray-800">
38+
<h4 className="mb-3 font-semibold text-gray-900 dark:text-white">👨‍💻 具备编程技能?</h4>
39+
<p className="text-sm text-gray-600 dark:text-gray-300">
40+
您可以参考我们的
41+
<a
42+
href="https://github.com/Kaiyiwing/qwerty-learner/blob/master/docs/toBuildDict.md"
43+
className="mx-1 font-medium text-blue-500 hover:text-blue-600"
44+
target="_blank"
45+
rel="noreferrer"
46+
>
47+
词典贡献指南
48+
</a>
49+
,为开源项目贡献新的词典内容。
50+
</p>
51+
</div>
52+
53+
<div className="rounded-lg bg-gradient-to-r from-blue-50 to-indigo-50 p-4 shadow-sm dark:from-gray-800 dark:to-gray-700">
54+
<h4 className="mb-3 font-semibold text-gray-900 dark:text-white">🚀 尝试 QwertyLearner.ai</h4>
55+
<p className="mb-3 text-sm text-gray-600 dark:text-gray-300">
56+
不会编程?想拥有自己的专属学习词典?操作简单,一键上传,点击即用
57+
<br />
58+
<div className="my-2"></div>
59+
那么,推荐您尝试由英国 DeepLearningAI 专业团队开发运营的
60+
<span className="mx-1 font-semibold text-blue-600 dark:text-blue-400">QwertyLearner.ai</span>
61+
</p>
62+
<div className="space-y-2 text-sm">
63+
<div className="flex items-center">
64+
<span className="mr-2 text-blue-500"></span>
65+
<span className="text-gray-700 dark:text-gray-300">
66+
<strong>AI 智能词库</strong> - 一键上传,智能生成释义和词性,打造专属自定义词库
67+
</span>
68+
</div>
69+
<div className="flex items-center">
70+
<span className="mr-2 text-blue-500"></span>
71+
<span className="text-gray-700 dark:text-gray-300">
72+
<strong>文章练习</strong> - 自定义文章内容,提升实战能力
73+
</span>
74+
</div>
75+
<div className="flex items-center">
76+
<span className="mr-2 text-blue-500"></span>
77+
<span className="text-gray-700 dark:text-gray-300">
78+
<strong>云端同步</strong> - 多设备练习记录、错题库同步
79+
</span>
80+
</div>
81+
<div className="flex items-center">
82+
<span className="mr-2 text-blue-500"></span>
83+
<span className="text-gray-700 dark:text-gray-300">
84+
<strong>词典选择</strong> - 更多丰富的专业词库
85+
</span>
86+
</div>
87+
</div>
88+
<button
89+
onClick={() => {
90+
window.open('https://qwertylearner.ai', '_blank')
91+
onClosePanel()
92+
}}
93+
className="mt-4 w-full transform rounded-lg bg-gradient-to-r from-blue-500 to-purple-600 px-4 py-2 text-sm font-semibold text-white transition-all duration-200 hover:scale-105 hover:from-blue-600 hover:to-purple-700 hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
94+
>
95+
🚀 体验 QwertyLearner.ai
96+
</button>
97+
</div>
98+
99+
<div className="rounded-lg bg-amber-50 p-3 text-xs text-amber-800 dark:bg-amber-900/20 dark:text-amber-200">
100+
<p>
101+
<strong>说明:</strong>QwertyLearner.ai 由英国 DeepLearningAI 独立开发运营,为开源版 QwertyLearner
102+
的独立衍生版本,开源版将持续维持开源与开放运营。
103+
</p>
104+
</div>
105+
</div>
47106
</InfoPanel>
48107
)}
49-
<button className="cursor-pointer pr-6 text-sm text-indigo-500" onClick={onOpenPanel}>
50-
没有找到想要的词典?
108+
<button
109+
onClick={onOpenPanel}
110+
className="group flex items-center space-x-2 rounded-lg border border-indigo-200 bg-gradient-to-r from-indigo-50 to-blue-50 px-4 py-2.5 text-sm font-medium text-indigo-600 shadow-sm transition-all duration-200 hover:scale-105 hover:border-indigo-300 hover:from-indigo-100 hover:to-blue-100 hover:shadow-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:border-indigo-400 dark:from-gray-800 dark:to-gray-700 dark:text-indigo-400 dark:hover:from-gray-700 dark:hover:to-gray-600"
111+
>
112+
<IconBook2 className="h-4 w-4" />
113+
<span>寻找更多词典</span>
114+
<span className="transform transition-transform group-hover:translate-x-1"></span>
51115
</button>
52116
</>
53117
)

src/pages/Gallery-N/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default function GalleryPage() {
6767
<IconX className="absolute right-20 top-10 mr-2 h-7 w-7 cursor-pointer text-gray-400" onClick={onBack} />
6868
<div className="mt-20 flex w-full flex-1 flex-col items-center justify-center overflow-y-auto">
6969
<div className="flex h-full flex-col overflow-y-auto">
70-
<div className="flex h-20 w-full items-center justify-between pb-6">
70+
<div className="flex h-20 w-full items-center justify-between pb-6 pr-20">
7171
<LanguageTabSwitcher />
7272
<DictRequest />
7373
</div>

src/pages/Typing/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import { useConfetti } from './hooks/useConfetti'
1111
import { useWordList } from './hooks/useWordList'
1212
import { TypingContext, TypingStateActionType, initialState, typingReducer } from './store'
1313
import { DonateCard } from '@/components/DonateCard'
14+
import EnhancedPromotionModal from '@/components/EnhancedPromotionModal'
1415
import Header from '@/components/Header'
15-
import StarCard from '@/components/StarCard'
1616
import Tooltip from '@/components/Tooltip'
1717
import { idDictionaryMap } from '@/resources/dictionary'
1818
import { currentChapterAtom, currentDictIdAtom, isReviewModeAtom, randomConfigAtom, reviewModeInfoAtom } from '@/store'
@@ -129,7 +129,7 @@ const App: React.FC = () => {
129129

130130
return (
131131
<TypingContext.Provider value={{ state: state, dispatch }}>
132-
<StarCard />
132+
<EnhancedPromotionModal />
133133
{state.isFinished && <DonateCard />}
134134
{state.isFinished && <ResultScreen />}
135135
<Layout>

src/store/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,8 @@ export const wordDictationConfigAtom = atomForConfig('wordDictationConfig', {
110110

111111
export const dismissStartCardDateAtom = atomWithStorage<Date | null>(DISMISS_START_CARD_DATE_KEY, null)
112112

113+
// Enhanced version promotion popup state
114+
export const hasSeenEnhancedPromotionAtom = atomWithStorage('hasSeenEnhancedPromotion', false)
115+
113116
// for dev test
114117
// dismissStartCardDateAtom = atom<Date | null>(new Date())

0 commit comments

Comments
 (0)