通知增加区域switch,增加免责声明

This commit is contained in:
mtvpls
2026-07-01 10:21:51 +08:00
parent 94f4c38575
commit ba34019bde
2 changed files with 410 additions and 129 deletions
+188 -126
View File
@@ -2,6 +2,7 @@
import { Bell, Info, Mail, MonitorSmartphone, Send, X } from 'lucide-react';
import { createPortal } from 'react-dom';
import { useState } from 'react';
interface EmailSettingsPanelProps {
isOpen: boolean;
@@ -30,6 +31,8 @@ interface EmailSettingsPanelProps {
statusType?: 'success' | 'error' | null;
}
type NotificationTab = 'email' | 'push' | 'telegram';
function Toggle({
checked,
disabled,
@@ -96,11 +99,21 @@ export function EmailSettingsPanel({
}: EmailSettingsPanelProps) {
if (!isOpen || !mounted) return null;
const [notificationTab, setNotificationTab] = useState<NotificationTab>('email');
const pushDisabled =
emailSettingsSaving ||
pushNotificationsBusy ||
(!pushNotifications && (!pushNotificationsConfigured || !pushNotificationsSupported));
const telegramNotifyEnabled = Boolean(telegramEnabled);
const tabs = [
{ key: 'email' as const, label: '邮件通知', icon: Mail },
{ key: 'push' as const, label: '浏览器通知', icon: MonitorSmartphone },
{ key: 'telegram' as const, label: 'Telegram', icon: Send },
];
return createPortal(
<>
<div
@@ -139,6 +152,30 @@ export function EmailSettingsPanel({
</button>
</div>
{/* 三区域切换式导航 — 与电视访问一致 */}
<div className='mb-5 grid grid-cols-3 rounded-2xl bg-gray-100 p-1 dark:bg-white/10'>
{tabs.map((item) => {
const Icon = item.icon;
const active = notificationTab === item.key;
return (
<button
key={item.key}
type='button'
onClick={() => setNotificationTab(item.key)}
className={`flex cursor-pointer items-center justify-center gap-1.5 rounded-xl px-3 py-2.5 text-sm font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/70 ${
active
? 'bg-white text-gray-900 shadow-sm dark:bg-gray-950 dark:text-white'
: 'text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white'
}`}
>
<Icon className='h-4 w-4' />
<span className='hidden sm:inline'>{item.label}</span>
<span className='sm:hidden'>{item.key === 'email' ? '邮件' : item.key === 'push' ? '浏览器' : 'Telegram'}</span>
</button>
);
})}
</div>
{emailSettingsLoading ? (
<div className='space-y-4' aria-live='polite'>
<div className='animate-pulse rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800'>
@@ -154,147 +191,172 @@ export function EmailSettingsPanel({
</p>
</div>
) : (
<div className='space-y-4'>
<section className='rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800'>
<div className='mb-4 flex items-start gap-3'>
<div className='flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-sky-100 text-sky-600 dark:bg-sky-900/30 dark:text-sky-300'>
<Mail className='h-5 w-5' />
</div>
<div className='min-w-0 flex-1'>
<h4 className='text-base font-semibold text-gray-900 dark:text-gray-100'>
</h4>
<p className='mt-1 text-sm text-gray-500 dark:text-gray-400'>
</p>
</div>
</div>
<label className='mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300'>
</label>
<input
type='email'
value={userEmail}
onChange={(e) => onUserEmailChange(e.target.value)}
placeholder='输入您的邮箱地址'
disabled={emailSettingsSaving}
className='mb-4 w-full rounded-xl border border-gray-300 bg-white px-3 py-2.5 text-sm text-gray-900 transition-colors placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-600 dark:bg-gray-900 dark:text-white'
/>
<div className='flex items-center justify-between gap-4 rounded-xl bg-white p-3 dark:bg-gray-900/70'>
<div>
<h5 className='text-sm font-medium text-gray-800 dark:text-gray-200'>
</h5>
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
</p>
</div>
<Toggle
checked={emailNotifications}
disabled={emailSettingsSaving}
label='切换收藏更新邮件通知'
onChange={() => onEmailNotificationsChange(!emailNotifications)}
/>
</div>
</section>
<section className='rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800'>
<div className='mb-4 flex items-start gap-3'>
<div className='flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-indigo-100 text-indigo-600 dark:bg-indigo-900/30 dark:text-indigo-300'>
<MonitorSmartphone className='h-5 w-5' />
</div>
<div className='min-w-0 flex-1'>
<h4 className='text-base font-semibold text-gray-900 dark:text-gray-100'>
</h4>
<p className='mt-1 text-sm text-gray-500 dark:text-gray-400'>
</p>
</div>
<Toggle
checked={pushNotifications}
disabled={pushDisabled}
busy={pushNotificationsBusy}
label='切换当前设备浏览器系统通知'
onChange={() => onPushNotificationsChange(!pushNotifications)}
/>
</div>
<div className='space-y-2 rounded-xl bg-white p-3 dark:bg-gray-900/70'>
<div className='flex items-center justify-between gap-3 text-sm'>
<span className='text-gray-600 dark:text-gray-400'></span>
<span className={`font-medium ${pushNotificationsSupported ? 'text-green-600 dark:text-green-400' : 'text-amber-600 dark:text-amber-400'}`}>
{pushNotificationsSupported ? '可用' : '需支持或授权'}
</span>
</div>
{!pushNotificationsConfigured && (
<p className='text-xs text-amber-600 dark:text-amber-400' role='alert'>
Web Push
</p>
)}
{pushNotificationsConfigured && !pushNotificationsSupported && (
<p className='text-xs text-amber-600 dark:text-amber-400' role='alert'>
</p>
)}
</div>
</section>
{telegramEnabled && (
<>
{/* 邮件通知 */}
{notificationTab === 'email' && (
<section className='rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800'>
<div className='mb-4 flex items-start gap-3'>
<div className='flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-sky-100 text-sky-600 dark:bg-sky-900/30 dark:text-sky-300'>
<Send className='h-5 w-5' />
<Mail className='h-5 w-5' />
</div>
<div className='min-w-0 flex-1'>
<h4 className='text-base font-semibold text-gray-900 dark:text-gray-100'>Telegram Bot </h4>
<h4 className='text-base font-semibold text-gray-900 dark:text-gray-100'>
</h4>
<p className='mt-1 text-sm text-gray-500 dark:text-gray-400'>
Telegram 使 Telegram
</p>
</div>
</div>
<div className='space-y-3 rounded-xl bg-white p-3 dark:bg-gray-900/70'>
<label className='mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300'>
</label>
<input
type='email'
value={userEmail}
onChange={(e) => onUserEmailChange(e.target.value)}
placeholder='输入您的邮箱地址'
disabled={emailSettingsSaving}
className='mb-4 w-full rounded-xl border border-gray-300 bg-white px-3 py-2.5 text-sm text-gray-900 transition-colors placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-600 dark:bg-gray-900 dark:text-white'
/>
<div className='flex items-center justify-between gap-4 rounded-xl bg-white p-3 dark:bg-gray-900/70'>
<div>
<h5 className='text-sm font-medium text-gray-800 dark:text-gray-200'>
</h5>
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
</p>
</div>
<Toggle
checked={emailNotifications}
disabled={emailSettingsSaving}
label='切换收藏更新邮件通知'
onChange={() => onEmailNotificationsChange(!emailNotifications)}
/>
</div>
</section>
)}
{/* 浏览器系统通知 */}
{notificationTab === 'push' && (
<section className='rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800'>
<div className='mb-4 flex items-start gap-3'>
<div className='flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-indigo-100 text-indigo-600 dark:bg-indigo-900/30 dark:text-indigo-300'>
<MonitorSmartphone className='h-5 w-5' />
</div>
<div className='min-w-0 flex-1'>
<h4 className='text-base font-semibold text-gray-900 dark:text-gray-100'>
</h4>
<p className='mt-1 text-sm text-gray-500 dark:text-gray-400'>
</p>
</div>
<Toggle
checked={pushNotifications}
disabled={pushDisabled}
busy={pushNotificationsBusy}
label='切换当前设备浏览器系统通知'
onChange={() => onPushNotificationsChange(!pushNotifications)}
/>
</div>
<div className='space-y-2 rounded-xl bg-white p-3 dark:bg-gray-900/70'>
<div className='flex items-center justify-between gap-3 text-sm'>
<span className='text-gray-600 dark:text-gray-400'></span>
<span className={`font-medium ${telegramBound ? 'text-green-600 dark:text-green-400' : 'text-amber-600 dark:text-amber-400'}`}>
{telegramBound ? `已绑定${telegramUsername ? ` @${telegramUsername}` : ''}` : '未绑定'}
<span className='text-gray-600 dark:text-gray-400'></span>
<span className={`font-medium ${pushNotificationsSupported ? 'text-green-600 dark:text-green-400' : 'text-amber-600 dark:text-amber-400'}`}>
{pushNotificationsSupported ? '可用' : '需支持或授权'}
</span>
</div>
{!telegramBound && (
<>
{telegramBindCode && (
<div className='rounded-lg bg-sky-50 p-3 text-sm text-sky-800 dark:bg-sky-900/30 dark:text-sky-200'>
<span className='font-mono text-base font-bold'>{telegramBindCode}</span>
<p className='mt-1 text-xs'> Bot /bind {telegramBindCode} Telegram</p>
</div>
)}
<div className='flex gap-2'>
<button
type='button'
onClick={onCreateTelegramBindCode}
disabled={telegramBindingBusy}
className='flex-1 rounded-lg bg-sky-600 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-sky-700 disabled:cursor-not-allowed disabled:bg-sky-400'
>
{telegramBindingBusy ? '生成中...' : '生成绑定码'}
</button>
{telegramDeepLink && (
<button
type='button'
onClick={() => window.open(telegramDeepLink, '_blank', 'noopener,noreferrer')}
className='flex-1 rounded-lg border border-sky-300 px-3 py-2 text-sm font-medium text-sky-700 transition-colors hover:bg-sky-50 dark:border-sky-700 dark:text-sky-300 dark:hover:bg-sky-900/30'
>
Telegram
</button>
)}
</div>
</>
{!pushNotificationsConfigured && (
<p className='text-xs text-amber-600 dark:text-amber-400' role='alert'>
Web Push
</p>
)}
{pushNotificationsConfigured && !pushNotificationsSupported && (
<p className='text-xs text-amber-600 dark:text-amber-400' role='alert'>
</p>
)}
</div>
</section>
)}
{/* Telegram 通知 */}
{notificationTab === 'telegram' && (
<>
{telegramNotifyEnabled ? (
<section className='rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800'>
<div className='mb-4 flex items-start gap-3'>
<div className='flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-sky-100 text-sky-600 dark:bg-sky-900/30 dark:text-sky-300'>
<Send className='h-5 w-5' />
</div>
<div className='min-w-0 flex-1'>
<h4 className='text-base font-semibold text-gray-900 dark:text-gray-100'>Telegram Bot </h4>
<p className='mt-1 text-sm text-gray-500 dark:text-gray-400'>
Telegram 使 Telegram
</p>
</div>
</div>
<div className='space-y-3 rounded-xl bg-white p-3 dark:bg-gray-900/70'>
<div className='flex items-center justify-between gap-3 text-sm'>
<span className='text-gray-600 dark:text-gray-400'></span>
<span className={`font-medium ${telegramBound ? 'text-green-600 dark:text-green-400' : 'text-amber-600 dark:text-amber-400'}`}>
{telegramBound ? `已绑定${telegramUsername ? ` @${telegramUsername}` : ''}` : '未绑定'}
</span>
</div>
{!telegramBound && (
<>
{telegramBindCode && (
<div className='rounded-lg bg-sky-50 p-3 text-sm text-sky-800 dark:bg-sky-900/30 dark:text-sky-200'>
<span className='font-mono text-base font-bold'>{telegramBindCode}</span>
<p className='mt-1 text-xs'> Bot /bind {telegramBindCode} Telegram</p>
</div>
)}
<div className='flex gap-2'>
<button
type='button'
onClick={onCreateTelegramBindCode}
disabled={telegramBindingBusy}
className='flex-1 rounded-lg bg-sky-600 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-sky-700 disabled:cursor-not-allowed disabled:bg-sky-400'
>
{telegramBindingBusy ? '生成中...' : '生成绑定码'}
</button>
{telegramDeepLink && (
<button
type='button'
onClick={() => window.open(telegramDeepLink, '_blank', 'noopener,noreferrer')}
className='flex-1 rounded-lg border border-sky-300 px-3 py-2 text-sm font-medium text-sky-700 transition-colors hover:bg-sky-50 dark:border-sky-700 dark:text-sky-300 dark:hover:bg-sky-900/30'
>
Telegram
</button>
)}
</div>
</>
)}
</div>
</section>
) : (
<section className='rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800'>
<div className='mb-4 flex items-start gap-3'>
<div className='flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400'>
<Send className='h-5 w-5' />
</div>
<div className='min-w-0 flex-1'>
<h4 className='text-base font-semibold text-gray-900 dark:text-gray-100'>Telegram Bot </h4>
<p className='mt-1 text-sm text-gray-500 dark:text-gray-400'>
Telegram Bot
</p>
</div>
</div>
</section>
)}
</>
)}
<button
onClick={onSave}
disabled={emailSettingsSaving}
@@ -322,7 +384,7 @@ export function EmailSettingsPanel({
{statusMessage}
</p>
) : null}
</div>
</>
)}
<div className='mt-6 flex gap-2 rounded-xl border border-blue-200 bg-blue-50 p-3 dark:border-blue-800 dark:bg-blue-900/20'>
@@ -336,4 +398,4 @@ export function EmailSettingsPanel({
</>,
document.body
);
}
}