This document provides a comprehensive guide for completing the push notification system, including server-side tasks, suggested opportunities, and environment configuration.
- Bell icon integrated into platforms list (now positioned as the last icon)
- Welcome notification on subscription
- Browser support detection
- Push subscription management (subscribe/unsubscribe)
- Service worker for handling push events
- Conditional logging (only in dev mode)
- Test notification keyboard hotkey (Ctrl+Shift+P or Cmd+Shift+P)
- Episode artwork support in notifications
- Environment variable support for VAPID keys and API URLs
All notification image paths are valid:
- ✅
/android-chrome-192x192.png- EXISTS (used for notification icon) - ✅
/favicon-32x32.png- EXISTS (used for notification badge) - ✅
/android-chrome-384x384.png- EXISTS (used for test episode art)
Add these to your .env file:
# VAPID Keys for Push Notifications
# Generate using: npx web-push generate-vapid-keys
PUBLIC_VAPID_KEY=your-public-vapid-key-here
VAPID_PRIVATE_KEY=your-private-vapid-key-here
# API URL for subscription management
PUBLIC_API_URL=https://your-api-domain.com# Install web-push if not already installed
npm install -g web-push
# Generate VAPID keys
npx web-push generate-vapid-keys
# Add the keys to your environment variablesPurpose: Store push subscription in database
Request Body:
{
"endpoint": "https://fcm.googleapis.com/fcm/send/...",
"keys": {
"p256dh": "...",
"auth": "..."
}
}Response:
{
"success": true,
"message": "Subscription saved"
}Implementation Tasks:
- Create database table for subscriptions
- Validate subscription data
- Store subscription with user identification (if applicable)
- Handle duplicate subscriptions
- Return appropriate error codes
Purpose: Remove push subscription from database
Request Body:
{
"endpoint": "https://fcm.googleapis.com/fcm/send/..."
}Response:
{
"success": true,
"message": "Subscription removed"
}Implementation Tasks:
- Find and delete subscription by endpoint
- Handle cases where subscription doesn't exist
- Return appropriate status codes
Purpose: Detect new episodes and trigger push notifications
Implementation Tasks:
- Create scheduled job to check RSS feed periodically (e.g., every 15-30 minutes)
- Compare current episodes with last checked state
- Detect new episodes
- Extract episode metadata (title, description, image, URL)
- Trigger push notifications for all subscribers
Suggested Technologies:
- Cron job or scheduled task
- RSS parser library (e.g.,
rss-parserfor Node.js) - Database to track last checked episode
Example Pseudocode:
async function checkForNewEpisodes() {
const feed = await parseFeed(RSS_FEED_URL);
const latestEpisode = feed.items[0];
const lastCheckedEpisode = await getLastCheckedEpisode();
if (latestEpisode.id !== lastCheckedEpisode.id) {
// New episode detected!
await sendPushNotificationToAllSubscribers({
title: `New Episode: ${latestEpisode.title}`,
body: latestEpisode.description,
icon: '/android-chrome-192x192.png',
badge: '/favicon-32x32.png',
image: latestEpisode.imageUrl,
url: `/${latestEpisode.slug}`,
tag: 'new-episode'
});
await updateLastCheckedEpisode(latestEpisode);
}
}Purpose: Send push notifications to subscribers
Implementation Tasks:
- Use web-push library to send notifications
- Retrieve all active subscriptions from database
- Send notification to each subscription
- Handle failed deliveries (expired/invalid subscriptions)
- Remove invalid subscriptions from database
- Implement rate limiting if needed
Example Code (Node.js):
const webpush = require('web-push');
// Configure VAPID details
webpush.setVapidDetails(
'mailto:your-email@example.com',
process.env.PUBLIC_VAPID_KEY,
process.env.VAPID_PRIVATE_KEY
);
async function sendNotificationToSubscriber(subscription, payload) {
try {
await webpush.sendNotification(subscription, JSON.stringify(payload));
return { success: true };
} catch (error) {
if (error.statusCode === 410) {
// Subscription expired - remove from database
await removeSubscription(subscription.endpoint);
}
return { success: false, error };
}
}
async function sendPushNotificationToAllSubscribers(notificationData) {
const subscriptions = await getAllSubscriptions();
const results = await Promise.allSettled(
subscriptions.map(sub =>
sendNotificationToSubscriber(sub, notificationData)
)
);
return results;
}Subscriptions Table:
CREATE TABLE push_subscriptions (
id SERIAL PRIMARY KEY,
endpoint TEXT UNIQUE NOT NULL,
p256dh TEXT NOT NULL,
auth TEXT NOT NULL,
user_id INTEGER, -- Optional: if you want to track per-user
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_notification_at TIMESTAMP
);
CREATE INDEX idx_endpoint ON push_subscriptions(endpoint);
CREATE INDEX idx_user_id ON push_subscriptions(user_id);Episodes Tracking Table:
CREATE TABLE episode_notifications (
id SERIAL PRIMARY KEY,
episode_id TEXT UNIQUE NOT NULL,
episode_title TEXT NOT NULL,
notified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Implementation Tasks:
- Log all push notification attempts
- Track success/failure rates
- Set up alerts for high failure rates
- Monitor subscription database size
- Track notification engagement (clicks)
Opportunity: Pre-announcement notifications
- Send notification 1 hour before scheduled episode release
- "New episode dropping in 1 hour!"
- Build anticipation and improve immediate engagement
Opportunity: Content teasers
- Send interesting quotes or highlights from recent episodes
- Can be scheduled 1-2 days after release
- Increases engagement with older content
Opportunity: Topical grouping
- When starting a new series or theme
- "New series alert: AI Deep Dives"
- Help listeners follow multi-episode storylines
Opportunity: Celebrity/Notable guest hype
- Announce special guests before episode release
- "Tomorrow: Interview with [Notable Person]"
- Leverage guest's fanbase
Opportunity: Real-time engagement
- If you do live recordings or streams
- Notify subscribers when going live
- Build community engagement
Opportunity: Community building
- Episode 100, 1M downloads, etc.
- "We hit 100 episodes! Thank you!"
- Strengthen listener relationship
Opportunity: Personalization
- Allow users to choose notification types
- Frequency preferences (all episodes, weekly digest, etc.)
- Episode categories of interest
- Implement preference management UI
Opportunity: Optimal delivery timing
- Send notifications at optimal times based on user location
- Avoid late night notifications
- Improve engagement rates
Opportunity: Direct actions
- Add action buttons to notifications:
- "Listen Now"
- "Remind Me Later"
- "Share"
- Quick engagement without opening browser
Opportunity: Monetization
- Occasional sponsor highlights (with clear opt-in)
- Special offers for listeners
- Revenue generation while respecting user experience
Opportunity: Content discovery
- "Based on what you liked: Episode X"
- Help users discover older content
- Increase overall listening time
Opportunity: Two-way communication
- Ask for feedback: "Rate this episode"
- Polls or questions related to episode topics
- Build community participation
- ✅ Subscribe to notifications
- ✅ Receive welcome notification
- ✅ Test keyboard shortcut (Ctrl+Shift+P / Cmd+Shift+P)
- ✅ Verify test notification displays correctly with episode art
⚠️ Unsubscribe from notifications (requires server API)⚠️ Test actual episode notification (requires server implementation)
- Unit tests for client-side components ✅
- Integration tests for API endpoints (TODO)
- End-to-end tests for subscription flow (TODO)
- VAPID Keys: Keep private key secure, never expose in client code
- Rate Limiting: Implement rate limits on subscription endpoints
- Validation: Validate all subscription data
- HTTPS Only: Push notifications require HTTPS
- User Privacy: Store minimal data, respect user preferences
- Spam Prevention: Limit notification frequency
- Batch Processing: Send notifications in batches to avoid overwhelming server
- Retry Logic: Implement exponential backoff for failed deliveries
- Database Indexing: Index endpoint field for fast lookups
- Caching: Cache subscription list between notifications
- Queue System: Use message queue for large subscriber bases (e.g., Redis, RabbitMQ)
- User Consent: Always get explicit permission
- Easy Unsubscribe: Make it simple to opt-out
- Clear Communication: Be transparent about notification types
- Frequency Limits: Don't spam users
- Value Delivery: Only send notifications users will appreciate
- Accessibility: Ensure notifications are accessible
- Generate and configure VAPID keys
- Set up database for subscriptions
- Create
/api/subscribeand/api/unsubscribeendpoints - Implement RSS feed monitor
- Build notification sender service
- Add error handling and monitoring
- Test end-to-end flow in production
- Implement user preferences system
- Add notification analytics
- Explore advanced features (see opportunities above)