返回 AiToEarn
1 /**
2 * WechatBrowserOverlay - 全局 App 内置浏览器打开提示蒙版
3 * 在微信、支付宝内置浏览器打开页面时,引导用户切换到系统浏览器继续使用。
4 */
5
6 'use client'
7
8 import { Ellipsis, Globe } from 'lucide-react'
9 import { useEffect, useState } from 'react'
10 import { useTransClient } from '@/app/i18n/client'
11 import { isBrowserOpenGuideWebView } from '@/utils/browser'
12
13 interface WechatBrowserOverlayProps {
14 variant?: 'themed' | 'standalone'
15 }
16
17 interface WechatBrowserOverlayCopy {
18 cornerHint: string
19 bubbleLine1: string
20 bubbleLine2: string
21 openInBrowser: string
22 description: string
23 }
24
25 export function WechatBrowserOverlay({ variant = 'themed' }: WechatBrowserOverlayProps) {
26 const { t } = useTransClient('common')
27 const [open, setOpen] = useState(false)
28
29 useEffect(() => {
30 setOpen(isBrowserOpenGuideWebView())
31 }, [])
32
33 if (!open)
34 return null
35
36 const copy = {
37 cornerHint: t('wechatBrowserMask.cornerHint'),
38 bubbleLine1: t('wechatBrowserMask.bubbleLine1'),
39 bubbleLine2: t('wechatBrowserMask.bubbleLine2'),
40 openInBrowser: t('wechatBrowserMask.openInBrowser'),
41 description: t('wechatBrowserMask.description'),
42 }
43
44 if (variant === 'standalone')
45 return <StandaloneWechatBrowserOverlay copy={copy} />
46
47 return (
48 <div
49 aria-label={copy.openInBrowser}
50 aria-modal="true"
51 role="dialog"
52 className="fixed inset-0 z-[1200] bg-slate-900/92 text-slate-50"
53 >
54 <div className="pointer-events-none absolute right-6 top-3 flex flex-col items-end gap-1.5 md:right-8 md:top-5">
55 <div className="rounded-full border border-slate-200/20 bg-slate-800/70 px-2.5 py-1 text-[11px] font-medium tracking-[0.08em] text-slate-100">
56 {copy.cornerHint}
57 </div>
58
59 <div className="relative h-16 w-16 text-slate-50 md:h-20 md:w-20">
60 <svg
61 aria-hidden="true"
62 viewBox="0 0 96 96"
63 className="h-full w-full overflow-visible drop-shadow-lg"
64 >
65 <defs>
66 <marker
67 id="wechat-browser-arrow-head"
68 markerWidth="10"
69 markerHeight="10"
70 refX="8"
71 refY="5"
72 orient="auto-start-reverse"
73 >
74 <path d="M0 0L9 5L0 10" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
75 </marker>
76 </defs>
77 <path
78 d="M14 74C28 71 42 62 54 47C64 35 71 23 78 11"
79 fill="none"
80 stroke="currentColor"
81 strokeWidth="4.5"
82 strokeLinecap="round"
83 strokeLinejoin="round"
84 strokeDasharray="7 7"
85 markerEnd="url(#wechat-browser-arrow-head)"
86 />
87 </svg>
88 </div>
89 </div>
90
91 <div className="flex min-h-full flex-col items-center justify-center px-6 pb-20 pt-24">
92 <div className="pointer-events-none flex w-full max-w-sm flex-col items-center">
93 <div className="rounded-[28px] border border-dashed border-slate-200/70 px-6 py-5 text-center text-lg leading-8 text-slate-50">
94 <p>{copy.bubbleLine1}</p>
95 <p>{copy.bubbleLine2}</p>
96 </div>
97
98 <div className="mt-8 flex items-center gap-4">
99 <div className="flex h-14 w-14 items-center justify-center rounded-full bg-slate-50 text-slate-900 shadow-lg">
100 <Ellipsis className="h-6 w-6" />
101 </div>
102 <div className="flex items-center gap-2 rounded-md bg-slate-100 px-4 py-3 text-sm font-semibold text-slate-900 shadow-lg">
103 <Globe className="h-4 w-4" />
104 <span>{copy.openInBrowser}</span>
105 </div>
106 </div>
107
108 <p className="mt-6 max-w-xs text-center text-sm leading-6 text-slate-200/90">
109 {copy.description}
110 </p>
111 </div>
112 </div>
113 </div>
114 )
115 }
116
117 function StandaloneWechatBrowserOverlay({ copy }: { copy: WechatBrowserOverlayCopy }) {
118 return (
119 <div
120 aria-label={copy.openInBrowser}
121 aria-modal="true"
122 role="dialog"
123 style={{
124 position: 'fixed',
125 inset: 0,
126 zIndex: 1200,
127 backgroundColor: 'rgb(15 23 42 / 0.92)',
128 color: 'rgb(248 250 252)',
129 }}
130 >
131 <div
132 style={{
133 pointerEvents: 'none',
134 position: 'absolute',
135 top: 12,
136 right: 24,
137 display: 'flex',
138 flexDirection: 'column',
139 alignItems: 'flex-end',
140 gap: 6,
141 }}
142 >
143 <div
144 style={{
145 borderRadius: 999,
146 border: '1px solid rgb(226 232 240 / 0.2)',
147 backgroundColor: 'rgb(30 41 59 / 0.7)',
148 padding: '4px 10px',
149 fontSize: 11,
150 fontWeight: 600,
151 letterSpacing: '0.08em',
152 color: 'rgb(241 245 249)',
153 }}
154 >
155 {copy.cornerHint}
156 </div>
157
158 <svg
159 aria-hidden="true"
160 viewBox="0 0 96 96"
161 style={{
162 width: 72,
163 height: 72,
164 overflow: 'visible',
165 color: 'rgb(248 250 252)',
166 }}
167 >
168 <defs>
169 <marker
170 id="wechat-browser-standalone-arrow-head"
171 markerWidth="10"
172 markerHeight="10"
173 refX="8"
174 refY="5"
175 orient="auto-start-reverse"
176 >
177 <path d="M0 0L9 5L0 10" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
178 </marker>
179 </defs>
180 <path
181 d="M14 74C28 71 42 62 54 47C64 35 71 23 78 11"
182 fill="none"
183 stroke="currentColor"
184 strokeWidth="4.5"
185 strokeLinecap="round"
186 strokeLinejoin="round"
187 strokeDasharray="7 7"
188 markerEnd="url(#wechat-browser-standalone-arrow-head)"
189 />
190 </svg>
191 </div>
192
193 <div
194 style={{
195 display: 'flex',
196 minHeight: '100%',
197 flexDirection: 'column',
198 alignItems: 'center',
199 justifyContent: 'center',
200 padding: '96px 24px 80px',
201 boxSizing: 'border-box',
202 fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
203 }}
204 >
205 <div style={{ pointerEvents: 'none', width: '100%', maxWidth: 384, textAlign: 'center' }}>
206 <div
207 style={{
208 borderRadius: 28,
209 border: '1px dashed rgb(226 232 240 / 0.7)',
210 padding: '20px 24px',
211 fontSize: 18,
212 lineHeight: 1.75,
213 }}
214 >
215 <p style={{ margin: 0 }}>{copy.bubbleLine1}</p>
216 <p style={{ margin: 0 }}>{copy.bubbleLine2}</p>
217 </div>
218
219 <div
220 style={{
221 display: 'flex',
222 alignItems: 'center',
223 justifyContent: 'center',
224 gap: 16,
225 marginTop: 32,
226 }}
227 >
228 <div
229 style={{
230 display: 'flex',
231 width: 56,
232 height: 56,
233 alignItems: 'center',
234 justifyContent: 'center',
235 borderRadius: 999,
236 backgroundColor: 'rgb(248 250 252)',
237 color: 'rgb(15 23 42)',
238 fontSize: 28,
239 fontWeight: 700,
240 boxShadow: '0 10px 24px rgb(2 6 23 / 0.35)',
241 }}
242 >
243
244 </div>
245 <div
246 style={{
247 display: 'flex',
248 alignItems: 'center',
249 gap: 8,
250 borderRadius: 8,
251 backgroundColor: 'rgb(241 245 249)',
252 color: 'rgb(15 23 42)',
253 padding: '12px 16px',
254 fontSize: 14,
255 fontWeight: 700,
256 boxShadow: '0 10px 24px rgb(2 6 23 / 0.35)',
257 }}
258 >
259 <span aria-hidden="true">🌐</span>
260 <span>{copy.openInBrowser}</span>
261 </div>
262 </div>
263
264 <p style={{ margin: '24px auto 0', maxWidth: 320, color: 'rgb(226 232 240 / 0.9)', fontSize: 14, lineHeight: 1.7 }}>
265 {copy.description}
266 </p>
267 </div>
268 </div>
269 </div>
270 )
271 }
272
272 lines Plain Text