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:
nmemmert committed 2026-01-16 10:35:37 -05:00
1 parent a3de1aaa1a
commit 618ab3a756
2 files changed
+118 -39

No files matched your search

+117 -38
View File
@@ -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>