feat: cross-platform prayer reminders (iOS, Android, Windows, Linux)
- Detect platform and disable Web Push for iOS (Safari limitation) - For iOS: Enable in-app reminders instead of push notifications - Add platform-specific UI messaging - Add test notification button for debugging - Track last reminder date in localStorage - Fix notification permission checks - Support all platforms: iOS (in-app), Android/Windows/Linux (push + in-app)
This commit is contained in:
1 parent
a3de1aaa1a
commit
618ab3a756
2 files changed
+118
-39
No files matched your search
+117
-38
@@ -7,6 +7,8 @@ interface AboutPageProps {
|
||||
userEmail?: string;
|
||||
}
|
||||
|
||||
type Platform = 'ios' | 'android' | 'windows' | 'linux' | 'macos' | 'other';
|
||||
|
||||
export default function AboutPage({ userEmail }: AboutPageProps) {
|
||||
const { logout, deleteAccount, sendVerificationEmail, user } = useAuth();
|
||||
const [showLogoutConfirm, setShowLogoutConfirm] = useState(false);
|
||||
@@ -21,10 +23,33 @@ export default function AboutPage({ userEmail }: AboutPageProps) {
|
||||
const [notificationsEnabled, setNotificationsEnabled] = useState(false);
|
||||
const [reminderTime, setReminderTime] = useState('09:00');
|
||||
const [notificationPermission, setNotificationPermission] = useState<NotificationPermission>('default');
|
||||
const [platform, setPlatform] = useState<Platform>('other');
|
||||
const [supportsWebPush, setSupportsWebPush] = useState(true);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Check current notification permission
|
||||
if ('Notification' in window) {
|
||||
// Detect platform
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
let detectedPlatform: Platform = 'other';
|
||||
let webPushSupport = true;
|
||||
|
||||
if (userAgent.includes('iphone') || userAgent.includes('ipad')) {
|
||||
detectedPlatform = 'ios';
|
||||
webPushSupport = false; // iOS Safari doesn't support Web Push
|
||||
} else if (userAgent.includes('android')) {
|
||||
detectedPlatform = 'android';
|
||||
} else if (userAgent.includes('windows')) {
|
||||
detectedPlatform = 'windows';
|
||||
} else if (userAgent.includes('linux')) {
|
||||
detectedPlatform = 'linux';
|
||||
} else if (userAgent.includes('mac')) {
|
||||
detectedPlatform = 'macos';
|
||||
}
|
||||
|
||||
setPlatform(detectedPlatform);
|
||||
setSupportsWebPush(webPushSupport);
|
||||
|
||||
// Check current notification permission for supported platforms
|
||||
if ('Notification' in window && webPushSupport) {
|
||||
setNotificationPermission(Notification.permission);
|
||||
}
|
||||
|
||||
@@ -33,29 +58,55 @@ export default function AboutPage({ userEmail }: AboutPageProps) {
|
||||
const savedTime = localStorage.getItem('prayerReminderTime') || '09:00';
|
||||
setNotificationsEnabled(savedEnabled);
|
||||
setReminderTime(savedTime);
|
||||
|
||||
// Schedule notification for supported platforms
|
||||
if (savedEnabled && webPushSupport) {
|
||||
scheduleNotification();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const requestNotificationPermission = async () => {
|
||||
if (!supportsWebPush) return;
|
||||
|
||||
if ('Notification' in window) {
|
||||
const permission = await Notification.requestPermission();
|
||||
setNotificationPermission(permission);
|
||||
if (permission === 'granted') {
|
||||
setNotificationsEnabled(true);
|
||||
localStorage.setItem('prayerRemindersEnabled', 'true');
|
||||
localStorage.setItem('lastPrayerReminderDate', new Date().toISOString());
|
||||
scheduleNotification();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const toggleNotifications = async () => {
|
||||
if (!notificationsEnabled && notificationPermission !== 'granted') {
|
||||
await requestNotificationPermission();
|
||||
if (supportsWebPush) {
|
||||
if (!notificationsEnabled && notificationPermission !== 'granted') {
|
||||
await requestNotificationPermission();
|
||||
} else {
|
||||
setNotificationsEnabled(!notificationsEnabled);
|
||||
localStorage.setItem('prayerRemindersEnabled', String(!notificationsEnabled));
|
||||
if (!notificationsEnabled) {
|
||||
localStorage.setItem('lastPrayerReminderDate', new Date().toISOString());
|
||||
scheduleNotification();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For iOS and unsupported platforms, just enable in-app reminders
|
||||
setNotificationsEnabled(!notificationsEnabled);
|
||||
localStorage.setItem('prayerRemindersEnabled', String(!notificationsEnabled));
|
||||
if (!notificationsEnabled) {
|
||||
scheduleNotification();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const testNotificationNow = async () => {
|
||||
if (supportsWebPush && Notification.permission === 'granted') {
|
||||
new Notification('🙏 Test Prayer Reminder', {
|
||||
body: 'This is a test notification. Your reminders are working!',
|
||||
icon: '/icon-192x192.png',
|
||||
badge: '/icon-192x192.png',
|
||||
tag: 'prayer-reminder-test',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,23 +119,25 @@ export default function AboutPage({ userEmail }: AboutPageProps) {
|
||||
};
|
||||
|
||||
const scheduleNotification = () => {
|
||||
if ('serviceWorker' in navigator && notificationsEnabled) {
|
||||
// Calculate time until next reminder
|
||||
const [hours, minutes] = reminderTime.split(':').map(Number);
|
||||
const now = new Date();
|
||||
const scheduledTime = new Date();
|
||||
scheduledTime.setHours(hours, minutes, 0, 0);
|
||||
|
||||
// If time has passed today, schedule for tomorrow
|
||||
if (scheduledTime <= now) {
|
||||
scheduledTime.setDate(scheduledTime.getDate() + 1);
|
||||
}
|
||||
|
||||
const timeUntilNotification = scheduledTime.getTime() - now.getTime();
|
||||
|
||||
// Schedule notification
|
||||
if (!supportsWebPush || !notificationsEnabled) return;
|
||||
|
||||
// Calculate time until next reminder
|
||||
const [hours, minutes] = reminderTime.split(':').map(Number);
|
||||
const now = new Date();
|
||||
const scheduledTime = new Date();
|
||||
scheduledTime.setHours(hours, minutes, 0, 0);
|
||||
|
||||
// If time has passed today, schedule for tomorrow
|
||||
if (scheduledTime <= now) {
|
||||
scheduledTime.setDate(scheduledTime.getDate() + 1);
|
||||
}
|
||||
|
||||
const timeUntilNotification = scheduledTime.getTime() - now.getTime();
|
||||
|
||||
// Only schedule if we have a valid time difference
|
||||
if (timeUntilNotification > 0) {
|
||||
setTimeout(() => {
|
||||
if (Notification.permission === 'granted') {
|
||||
if ('Notification' in window && Notification.permission === 'granted' && notificationsEnabled) {
|
||||
new Notification('🙏 Time to Pray', {
|
||||
body: 'Take a moment to connect with God and pray.',
|
||||
icon: '/icon-192x192.png',
|
||||
@@ -92,6 +145,10 @@ export default function AboutPage({ userEmail }: AboutPageProps) {
|
||||
tag: 'prayer-reminder',
|
||||
requireInteraction: false,
|
||||
});
|
||||
|
||||
// Save reminder shown timestamp
|
||||
localStorage.setItem('lastPrayerReminderDate', new Date().toISOString());
|
||||
|
||||
// Reschedule for next day
|
||||
setTimeout(scheduleNotification, 24 * 60 * 60 * 1000);
|
||||
}
|
||||
@@ -192,10 +249,20 @@ export default function AboutPage({ userEmail }: AboutPageProps) {
|
||||
<div className="border-b border-slate-700 pb-6">
|
||||
<h2 className="text-lg font-semibold text-white mb-4">Prayer Reminders</h2>
|
||||
<div className="space-y-4">
|
||||
{/* Platform Support Info */}
|
||||
{platform === 'ios' && (
|
||||
<div className="bg-blue-900 border border-blue-700 text-blue-200 px-3 py-2 rounded-lg text-xs">
|
||||
<p className="font-medium mb-1">📱 iOS: In-App Reminders</p>
|
||||
<p>iOS Safari doesn't support push notifications. Enable reminders to get a message when you open the app.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-300">Daily Reminders</p>
|
||||
<p className="text-xs text-slate-400">Get notified to pray each day</p>
|
||||
<p className="text-xs text-slate-400">
|
||||
{supportsWebPush ? 'Push notifications each day' : 'Reminders when you open the app'}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={toggleNotifications}
|
||||
@@ -211,26 +278,38 @@ export default function AboutPage({ userEmail }: AboutPageProps) {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{notificationPermission === 'denied' && (
|
||||
{notificationPermission === 'denied' && supportsWebPush && (
|
||||
<div className="bg-red-900 border border-red-700 text-red-200 px-3 py-2 rounded-lg text-xs">
|
||||
Notifications are blocked. Please enable them in your browser settings.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{notificationsEnabled && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-300 mb-2">
|
||||
Reminder Time
|
||||
</label>
|
||||
<input
|
||||
type="time"
|
||||
value={reminderTime}
|
||||
onChange={(e) => handleTimeChange(e.target.value)}
|
||||
className="w-full p-2 bg-slate-700 border border-slate-600 rounded-lg text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
<p className="text-xs text-slate-400 mt-2">
|
||||
You'll receive a notification at {reminderTime} each day
|
||||
</p>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-300 mb-2">
|
||||
Reminder Time
|
||||
</label>
|
||||
<input
|
||||
type="time"
|
||||
value={reminderTime}
|
||||
onChange={(e) => handleTimeChange(e.target.value)}
|
||||
className="w-full p-2 bg-slate-700 border border-slate-600 rounded-lg text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
<p className="text-xs text-slate-400 mt-2">
|
||||
You'll receive {supportsWebPush ? 'a notification' : 'a reminder'} at {reminderTime} each day
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Test Button for Web Push Platforms */}
|
||||
{supportsWebPush && notificationPermission === 'granted' && (
|
||||
<button
|
||||
onClick={testNotificationNow}
|
||||
className="w-full px-3 py-2 bg-slate-700 hover:bg-slate-600 text-slate-200 text-sm font-medium rounded-lg transition-colors border border-slate-600"
|
||||
>
|
||||
📬 Test Notification
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user