import { useState, useEffect } from "react"; import { messaging, initializeForegroundNotifications } from "../firebase.ts"; import { getToken, deleteToken } from "firebase/messaging"; import { getGameTitle } from "../utils.ts"; const VAPID_KEY = import.meta.env.VITE_VAPID_KEY; interface NotificationButtonProps { className?: string; isMoe?: boolean; gameId?: string; } export default function NotificationButton({ className = "", isMoe = false, gameId }: NotificationButtonProps) { const [permission, setPermission] = useState("default"); const [isRegistered, setIsRegistered] = useState(false); const [isSubscribed, setIsSubscribed] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { // Check initial permission status setPermission(Notification.permission); // Check if service worker is registered const checkRegistration = async () => { if ('serviceWorker' in navigator) { const registration = await navigator.serviceWorker.getRegistration('/firebase-messaging-sw.js'); setIsRegistered(!!registration); // Initialize foreground notifications if already registered if (registration && Notification.permission === "granted") { initializeForegroundNotifications(); } } }; // Check if subscribed to topic (for gameId mode) const checkTopicSubscription = () => { if (gameId) { const subscribedTopics = JSON.parse(localStorage.getItem('subscribed_topics') || '[]'); setIsSubscribed(subscribedTopics.includes(gameId)); } }; checkRegistration(); checkTopicSubscription(); }, [gameId]); const handleSubscribeToTopic = async () => { setLoading(true); setError(null); try { // Ensure notifications are enabled first const permissionResult = await Notification.requestPermission(); setPermission(permissionResult); if (permissionResult === "granted") { let registration = await navigator.serviceWorker.getRegistration('/firebase-messaging-sw.js'); if (!registration) { registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js'); console.log("Service Worker registered:", registration); } let token = localStorage.getItem('fcm_token'); if (!token) { token = await getToken(messaging, { vapidKey: VAPID_KEY, serviceWorkerRegistration: registration, }); localStorage.setItem('fcm_token', token); } // TODO: Subscribe to topic via backend API console.log(`Subscribing to topic: ${gameId} with token: ${token}`); await fetch(import.meta.env.VITE_MIDDLEWARE_BASE_URL+`/api/notifications/set?topic=${gameId}&token=${token}&action=subscribe`, { method: "GET", headers: { "Content-Type": "application/json" }, }); // Update local storage to track subscribed topics const subscribedTopics = JSON.parse(localStorage.getItem('subscribed_topics') || '[]'); if (!subscribedTopics.includes(gameId)) { subscribedTopics.push(gameId); localStorage.setItem('subscribed_topics', JSON.stringify(subscribedTopics)); } setIsSubscribed(true); setIsRegistered(true); } else { setError("Notification permission was denied"); } } catch (err) { console.error("Error subscribing to topic:", err); setError("Failed to subscribe to game notifications. Please try again."); } finally { setLoading(false); } }; const handleUnsubscribeFromTopic = async () => { setLoading(true); setError(null); try { const token = localStorage.getItem('fcm_token'); // TODO: Unsubscribe from topic via backend API console.log(`Unsubscribing from topic: ${gameId} with token: ${token}`); await fetch(import.meta.env.VITE_MIDDLEWARE_BASE_URL+`/api/notifications/set?topic=${gameId}&token=${token}&action=unsubscribe`, { method: "GET", headers: { "Content-Type": "application/json" }, }); // Update local storage to remove topic const subscribedTopics = JSON.parse(localStorage.getItem('subscribed_topics') || '[]'); const updatedTopics = subscribedTopics.filter((topic: string) => topic !== gameId); localStorage.setItem('subscribed_topics', JSON.stringify(updatedTopics)); setIsSubscribed(false); } catch (err) { console.error("Error unsubscribing from topic:", err); setError("Failed to unsubscribe from game notifications. Please try again."); } finally { setLoading(false); } }; const handleEnableNotifications = async () => { setLoading(true); setError(null); try { const permissionResult = await Notification.requestPermission(); setPermission(permissionResult); if (permissionResult === "granted") { let registration = await navigator.serviceWorker.getRegistration('/firebase-messaging-sw.js'); if (!registration) { registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js'); console.log("Service Worker registered:", registration); } else { console.log("Reusing existing Service Worker:", registration); } const token = await getToken(messaging, { vapidKey: VAPID_KEY, serviceWorkerRegistration: registration, }); console.log("FCM Token:", token); localStorage.setItem('fcm_token', token); // Foreground notification listener initializeForegroundNotifications(); setIsRegistered(true); } else { setError("Notification permission was denied"); } } catch (err) { console.error("Error enabling notifications:", err); setError("Failed to enable notifications. Please try again."); } finally { setLoading(false); } }; const handleDisableNotifications = async () => { setLoading(true); setError(null); try { await deleteToken(messaging); console.log("FCM token deleted"); localStorage.removeItem('fcm_token'); // Clear all subscribed topics localStorage.removeItem('subscribed_topics'); if ('serviceWorker' in navigator) { const registration = await navigator.serviceWorker.getRegistration('/firebase-messaging-sw.js'); if (registration) { await registration.unregister(); console.log("Service Worker unregistered"); } } setIsRegistered(false); setIsSubscribed(false); } catch (err) { console.error("Error disabling notifications:", err); setError("Failed to disable notifications. Please try again."); } finally { setLoading(false); } }; // Determine button content const getButtonContent = () => { if (loading) { return ( <> {gameId ? (isSubscribed ? "Unsubscribing..." : "Subscribing...") : (isRegistered ? "Disabling..." : "Enabling...") } ); } if (permission === "denied") { return ( <> Notifications Blocked ); } // For topic subscription mode if (gameId) { if (isSubscribed && permission === "granted") { return ( <> Unsubscribe from {getGameTitle(gameId)} ); } return ( <> Subscribe to {getGameTitle(gameId)} Updates ); } if (isRegistered && permission === "granted") { return ( <> Disable Notifications ); } return ( <> Enable Notifications ); }; const handleClick = () => { if (permission === "denied") { alert("Notifications are blocked. Please enable them in your browser settings."); return; } if (gameId) { if (permission !== "granted") { alert("Please enable general notifications first before subscribing to game updates."); return; } if (isSubscribed) { handleUnsubscribeFromTopic(); } else { handleSubscribeToTopic(); } } else { if (isRegistered && permission === "granted") { handleDisableNotifications(); } else { handleEnableNotifications(); } } }; const getButtonStyles = () => { if (loading || permission === "denied" || (gameId && permission !== "granted")) { return isMoe ? `bg-pink-300 cursor-not-allowed opacity-60` : `bg-gray-600 cursor-not-allowed opacity-60`; } const isActive = gameId ? isSubscribed : isRegistered; if (isMoe) { return isActive ? `bg-pink-600 text-white hover:bg-pink-700` : `bg-pink-500 text-white hover:bg-pink-600`; } else { return isActive ? `bg-purple-700 text-white hover:bg-purple-800` : `bg-purple-600 text-white hover:bg-purple-700`; } }; return (
{error && (

{error}

)} {permission === "denied" && (

To enable notifications, update your browser settings

)} {gameId && permission !== "granted" && permission !== "denied" && (

Enable general notifications first to subscribe to game updates

)}
); }