feat: Add Web Push notifications and multiple UX improvements
continuous-integration/drone/push Build is failing
continuous-integration/drone/push Build is failing
- Add PWA manifest and service worker for push notifications
- Implement VAPID key generation and push subscription management
- Add push notification API endpoints (/api/push/*)
- Add push_subscriptions table to database schema
- Update notification settings UI with push support and iOS hints
- Fix translation issue showing "{ task } created" - add missing keys
- Fix mobile sidebar visibility for iOS home screen app
- Change default task filter from "all" to "open" (excludes done tasks)
- Add "Open" filter option to show only todo + inProgress tasks
This commit is contained in:
@@ -22,32 +22,113 @@ import { DataExportCard } from '@/components/user/DataExportCard';
|
||||
|
||||
const NotificationSettings = () => {
|
||||
const { t } = useTranslation();
|
||||
const { enabled, toggleEnabled, permission, requestPermission } = useNotifications({ poll: false });
|
||||
const { toast } = useToast();
|
||||
const {
|
||||
enabled,
|
||||
toggleEnabled,
|
||||
permission,
|
||||
requestPermission,
|
||||
pushSupported,
|
||||
isStandalone,
|
||||
sendTestNotification,
|
||||
isLoading
|
||||
} = useNotifications({ poll: false });
|
||||
|
||||
const handleToggle = (checked: boolean) => {
|
||||
const handleToggle = async (checked: boolean) => {
|
||||
if (checked && permission !== 'granted') {
|
||||
requestPermission();
|
||||
const success = await requestPermission();
|
||||
if (success) {
|
||||
toast({
|
||||
title: t('notifications.enabledTitle'),
|
||||
description: t('notifications.enabledBody'),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
toggleEnabled(checked);
|
||||
await toggleEnabled(checked);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTestNotification = async () => {
|
||||
const success = await sendTestNotification();
|
||||
if (success) {
|
||||
toast({
|
||||
title: t('notifications.testSent', 'Test Sent'),
|
||||
description: t('notifications.testSentDesc', 'Check for a notification on your device.'),
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: t('common.error'),
|
||||
description: t('notifications.testFailed', 'Failed to send test notification.'),
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<p className="font-medium">{t('notifications.enableBrowser')}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{permission === 'denied' ?
|
||||
<span className="text-destructive">Permission denied by browser. Please reset site permissions.</span> :
|
||||
t('notifications.description')
|
||||
}
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
{/* Push Support Status */}
|
||||
{pushSupported && (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400">
|
||||
{t('notifications.pushSupported', 'Push Supported')}
|
||||
</span>
|
||||
{isStandalone && (
|
||||
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-violet-100 text-violet-800 dark:bg-violet-900/30 dark:text-violet-400">
|
||||
{t('notifications.installedApp', 'Installed App')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* iOS PWA Hint */}
|
||||
{!pushSupported && /iPhone|iPad|iPod/.test(navigator.userAgent) && !isStandalone && (
|
||||
<div className="p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800">
|
||||
<p className="text-sm text-amber-800 dark:text-amber-200">
|
||||
<strong>{t('notifications.iosHintTitle', 'Add to Home Screen')}</strong><br />
|
||||
{t('notifications.iosHintDesc', 'To receive push notifications on iOS, tap the Share button and select "Add to Home Screen", then open the app from there.')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Main Toggle */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<p className="font-medium">
|
||||
{pushSupported
|
||||
? t('notifications.enablePush', 'Push Notifications')
|
||||
: t('notifications.enableBrowser')}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{permission === 'denied' ? (
|
||||
<span className="text-destructive">
|
||||
{t('notifications.permissionDenied', 'Permission denied by browser. Please reset site permissions.')}
|
||||
</span>
|
||||
) : pushSupported ? (
|
||||
t('notifications.pushDescription', 'Receive notifications even when the app is closed.')
|
||||
) : (
|
||||
t('notifications.description')
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onCheckedChange={handleToggle}
|
||||
disabled={permission === 'denied' || isLoading}
|
||||
/>
|
||||
</div>
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onCheckedChange={handleToggle}
|
||||
disabled={permission === 'denied'}
|
||||
/>
|
||||
|
||||
{/* Test Button */}
|
||||
{enabled && pushSupported && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleTestNotification}
|
||||
disabled={isLoading}
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
{t('notifications.sendTest', 'Send Test Notification')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user