mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-05-29 07:25:33 +08:00
feat: introduce notification system and claude notifications (#450)
* feat: introduce notification system and claude notifications * fix(sw): prevent caching of API requests and WebSocket upgrades * default to false for webpush notifications and translations for the button * fix: notifications orchestrator and add a notification when first enabled * fix: remove unused state update and dependency in settings controller hook * fix: show notifications settings tab * fix: add notifications for response completion for all providers * feat: show session name in notification and don't reload tab on clicking --- the notification --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Haileyesus <something@gmail.com>
This commit is contained in:
145
src/components/settings/view/tabs/NotificationsSettingsTab.tsx
Normal file
145
src/components/settings/view/tabs/NotificationsSettingsTab.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import { Bell, BellOff, BellRing, Loader2 } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { NotificationPreferencesState } from '../../types/types';
|
||||
|
||||
type NotificationsSettingsTabProps = {
|
||||
notificationPreferences: NotificationPreferencesState;
|
||||
onNotificationPreferencesChange: (value: NotificationPreferencesState) => void;
|
||||
pushPermission: NotificationPermission | 'unsupported';
|
||||
isPushSubscribed: boolean;
|
||||
isPushLoading: boolean;
|
||||
onEnablePush: () => void;
|
||||
onDisablePush: () => void;
|
||||
};
|
||||
|
||||
export default function NotificationsSettingsTab({
|
||||
notificationPreferences,
|
||||
onNotificationPreferencesChange,
|
||||
pushPermission,
|
||||
isPushSubscribed,
|
||||
isPushLoading,
|
||||
onEnablePush,
|
||||
onDisablePush,
|
||||
}: NotificationsSettingsTabProps) {
|
||||
const { t } = useTranslation('settings');
|
||||
|
||||
const pushSupported = pushPermission !== 'unsupported';
|
||||
const pushDenied = pushPermission === 'denied';
|
||||
|
||||
return (
|
||||
<div className="space-y-6 md:space-y-8">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<Bell className="w-5 h-5 text-blue-600" />
|
||||
<h3 className="text-lg font-medium text-foreground">{t('notifications.title')}</h3>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">{t('notifications.description')}</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 bg-card border border-border rounded-lg p-4">
|
||||
<h4 className="font-medium text-foreground">{t('notifications.webPush.title')}</h4>
|
||||
{!pushSupported ? (
|
||||
<p className="text-sm text-muted-foreground">{t('notifications.webPush.unsupported')}</p>
|
||||
) : pushDenied ? (
|
||||
<p className="text-sm text-muted-foreground">{t('notifications.webPush.denied')}</p>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
disabled={isPushLoading}
|
||||
onClick={() => {
|
||||
if (isPushSubscribed) {
|
||||
onDisablePush();
|
||||
} else {
|
||||
onEnablePush();
|
||||
}
|
||||
}}
|
||||
className={`inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed ${
|
||||
isPushSubscribed
|
||||
? 'bg-red-100 text-red-700 hover:bg-red-200 dark:bg-red-900/30 dark:text-red-400 dark:hover:bg-red-900/50'
|
||||
: 'bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600'
|
||||
}`}
|
||||
>
|
||||
{isPushLoading ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
) : isPushSubscribed ? (
|
||||
<BellOff className="w-4 h-4" />
|
||||
) : (
|
||||
<BellRing className="w-4 h-4" />
|
||||
)}
|
||||
{isPushLoading
|
||||
? t('notifications.webPush.loading')
|
||||
: isPushSubscribed
|
||||
? t('notifications.webPush.disable')
|
||||
: t('notifications.webPush.enable')}
|
||||
</button>
|
||||
{isPushSubscribed && (
|
||||
<span className="text-sm text-green-600 dark:text-green-400">
|
||||
{t('notifications.webPush.enabled')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 bg-card border border-border rounded-lg p-4">
|
||||
<h4 className="font-medium text-foreground">{t('notifications.events.title')}</h4>
|
||||
<div className="space-y-3">
|
||||
<label className="flex items-center gap-2 text-sm text-foreground">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationPreferences.events.actionRequired}
|
||||
onChange={(event) =>
|
||||
onNotificationPreferencesChange({
|
||||
...notificationPreferences,
|
||||
events: {
|
||||
...notificationPreferences.events,
|
||||
actionRequired: event.target.checked,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
{t('notifications.events.actionRequired')}
|
||||
</label>
|
||||
|
||||
<label className="flex items-center gap-2 text-sm text-foreground">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationPreferences.events.stop}
|
||||
onChange={(event) =>
|
||||
onNotificationPreferencesChange({
|
||||
...notificationPreferences,
|
||||
events: {
|
||||
...notificationPreferences.events,
|
||||
stop: event.target.checked,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
{t('notifications.events.stop')}
|
||||
</label>
|
||||
|
||||
<label className="flex items-center gap-2 text-sm text-foreground">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationPreferences.events.error}
|
||||
onChange={(event) =>
|
||||
onNotificationPreferencesChange({
|
||||
...notificationPreferences,
|
||||
events: {
|
||||
...notificationPreferences.events,
|
||||
error: event.target.checked,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
{t('notifications.events.error')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -255,6 +255,7 @@ function ClaudePermissions({
|
||||
<li><code className="rounded bg-blue-100 px-1 dark:bg-blue-800">"Bash(rm:*)"</code> {t('permissions.toolExamples.bashRm')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user