aboutsummaryrefslogtreecommitdiffstats
path: root/site/src/components/NotificationButton.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'site/src/components/NotificationButton.tsx')
-rw-r--r--site/src/components/NotificationButton.tsx152
1 files changed, 141 insertions, 11 deletions
diff --git a/site/src/components/NotificationButton.tsx b/site/src/components/NotificationButton.tsx
index 66109a3..3e0342b 100644
--- a/site/src/components/NotificationButton.tsx
+++ b/site/src/components/NotificationButton.tsx
@@ -1,6 +1,7 @@
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 =
"BK7tpLF5Loy8Ew8bKxhTi-vOEJdxJSnu-jPyagWecLdD_SrEAt_OQS7nu0Xu3hR7AQpn0cOmgcdeeQd5zq5-Gyo";
@@ -8,11 +9,13 @@ const VAPID_KEY =
interface NotificationButtonProps {
className?: string;
isMoe?: boolean;
+ gameId?: string;
}
-export default function NotificationButton({ className = "", isMoe = false }: NotificationButtonProps) {
+export default function NotificationButton({ className = "", isMoe = false, gameId }: NotificationButtonProps) {
const [permission, setPermission] = useState<NotificationPermission>("default");
const [isRegistered, setIsRegistered] = useState(false);
+ const [isSubscribed, setIsSubscribed] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -33,8 +36,91 @@ export default function NotificationButton({ className = "", isMoe = false }: No
}
};
+ // 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}`);
+ // Stub for now - will make actual API call to backend to subscribe to topic
+
+ // 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}`);
+ // Stub for now - will make actual API call to backend to unsubscribe from topic
+
+ // 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);
@@ -94,6 +180,7 @@ export default function NotificationButton({ className = "", isMoe = false }: No
}
setIsRegistered(false);
+ setIsSubscribed(false);
} catch (err) {
console.error("Error disabling notifications:", err);
setError("Failed to disable notifications. Please try again.");
@@ -111,7 +198,10 @@ export default function NotificationButton({ className = "", isMoe = false }: No
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
- {isRegistered ? "Disabling..." : "Enabling..."}
+ {gameId
+ ? (isSubscribed ? "Unsubscribing..." : "Subscribing...")
+ : (isRegistered ? "Disabling..." : "Enabling...")
+ }
</>
);
}
@@ -127,6 +217,28 @@ export default function NotificationButton({ className = "", isMoe = false }: No
);
}
+ // For topic subscription mode
+ if (gameId) {
+ if (isSubscribed && permission === "granted") {
+ return (
+ <>
+ <svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24">
+ <path d="M5 13l4 4L19 7" />
+ </svg>
+ Unsubscribe from {getGameTitle(gameId)}
+ </>
+ );
+ }
+ return (
+ <>
+ <svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
+ </svg>
+ Subscribe to {getGameTitle(gameId)} Updates
+ </>
+ );
+ }
+
if (isRegistered && permission === "granted") {
return (
<>
@@ -154,27 +266,41 @@ export default function NotificationButton({ className = "", isMoe = false }: No
return;
}
- if (isRegistered && permission === "granted") {
- handleDisableNotifications();
+ if (gameId) {
+ if (permission !== "granted") {
+ alert("Please enable general notifications first before subscribing to game updates.");
+ return;
+ }
+
+ if (isSubscribed) {
+ handleUnsubscribeFromTopic();
+ } else {
+ handleSubscribeToTopic();
+ }
} else {
- handleEnableNotifications();
+ if (isRegistered && permission === "granted") {
+ handleDisableNotifications();
+ } else {
+ handleEnableNotifications();
+ }
}
};
- // Determine button styles
const getButtonStyles = () => {
- if (loading || permission === "denied") {
+ 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 isRegistered
+ return isActive
? `bg-pink-600 text-white hover:bg-pink-700`
: `bg-pink-500 text-white hover:bg-pink-600`;
} else {
- return isRegistered
+ return isActive
? `bg-purple-700 text-white hover:bg-purple-800`
: `bg-purple-600 text-white hover:bg-purple-700`;
}
@@ -184,7 +310,6 @@ export default function NotificationButton({ className = "", isMoe = false }: No
<div className="flex flex-col items-center gap-2">
<button
onClick={handleClick}
- disabled={loading || permission === "denied"}
className={`flex items-center justify-center px-4 py-2 rounded-lg font-semibold transition-colors ${getButtonStyles()} ${className}`}
>
{getButtonContent()}
@@ -199,6 +324,11 @@ export default function NotificationButton({ className = "", isMoe = false }: No
To enable notifications, update your browser settings
</p>
)}
+ {gameId && permission !== "granted" && permission !== "denied" && (
+ <p className={`text-xs ${isMoe ? "text-pink-600" : "text-gray-400"}`}>
+ Enable general notifications first to subscribe to game updates
+ </p>
+ )}
</div>
);
}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage