feat: integrate Firebase Cloud Messaging for push notifications
Some checks failed
Build frontend / build (push) Failing after 24s

This commit is contained in:
Claw AI
2026-03-31 19:50:48 +00:00
parent 81674c4aa7
commit 2bea2d190c
6 changed files with 1213 additions and 1 deletions

View File

@ -41,6 +41,7 @@ import { motion, AnimatePresence } from "framer-motion";
import AuthService from "./services/AuthService";
import { UserRole, UserRoleLabels } from "./enums/UserRole";
import "./i18n/config";
import NotificationHandler from "./components/NotificationHandler";
export default function ClientLayout({ children }) {
const { t, i18n } = useTranslation();
@ -798,6 +799,7 @@ export default function ClientLayout({ children }) {
</div>
</footer>
)}
<NotificationHandler />
</>
);
}

View File

@ -0,0 +1,53 @@
"use client";
import { useEffect, useState } from "react";
import { requestNotificationPermission, onForegroundMessage } from "../utils/firebase";
export default function NotificationHandler() {
const [notification, setNotification] = useState(null);
useEffect(() => {
// Request permission and get token
requestNotificationPermission().then((token) => {
if (token) {
console.log("[Notifications] FCM token obtained");
// TODO: Send token to your backend to register the device
// e.g. apiFetch('/Notifications/RegisterDevice', { method: 'POST', body: { token } })
}
});
// Listen for foreground messages
const unsubscribe = onForegroundMessage((payload) => {
const title = payload.notification?.title || payload.data?.title || "Sweet Home";
const body = payload.notification?.body || payload.data?.body || "";
setNotification({ title, body });
// Auto-dismiss after 5 seconds
setTimeout(() => setNotification(null), 5000);
});
return () => unsubscribe();
}, []);
if (!notification) return null;
return (
<div className="fixed bottom-4 left-4 right-4 md:left-auto md:right-4 md:w-96 bg-white rounded-xl shadow-2xl border border-gray-200 p-4 z-[9999] animate-slide-up">
<div className="flex items-start gap-3">
<div className="w-10 h-10 bg-amber-100 rounded-lg flex items-center justify-center flex-shrink-0">
<span className="text-xl">🏠</span>
</div>
<div className="flex-1 min-w-0">
<p className="font-bold text-gray-900 text-sm">{notification.title}</p>
<p className="text-gray-600 text-sm mt-0.5">{notification.body}</p>
</div>
<button
onClick={() => setNotification(null)}
className="text-gray-400 hover:text-gray-600 flex-shrink-0"
>
</button>
</div>
</div>
);
}

63
app/utils/firebase.js Normal file
View File

@ -0,0 +1,63 @@
import { initializeApp, getApps } from "firebase/app";
import { getMessaging, getToken, onMessage } from "firebase/messaging";
const firebaseConfig = {
apiKey: "AIzaSyBZV7KBLRJSTApahfrO8lBesmIM3zNRSaY",
authDomain: "sweet-home-b2766.firebaseapp.com",
projectId: "sweet-home-b2766",
storageBucket: "sweet-home-b2766.firebasestorage.app",
messagingSenderId: "602865114600",
appId: "1:602865114600:web:ed9b6754940507a6ab585d",
measurementId: "G-M2V95NBJLX",
};
// Initialize Firebase (avoid duplicate init in SSR)
const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];
// Get messaging instance (only works in browser)
let messaging = null;
if (typeof window !== "undefined" && "serviceWorker" in navigator) {
try {
messaging = getMessaging(app);
} catch (e) {
console.warn("[Firebase] Messaging init failed:", e.message);
}
}
// Request notification permission and get FCM token
export async function requestNotificationPermission() {
if (typeof window === "undefined") return null;
try {
const permission = await Notification.requestPermission();
if (permission !== "granted") {
console.log("[FCM] Notification permission denied");
return null;
}
const registration = await navigator.serviceWorker.register("/firebase-messaging-sw.js");
const token = await getToken(messaging, {
vapidKey: "BO0tGzMOqN3xQp8IG2wQEXwJKUJfx7T3eVvLq3HjC2Q", // TODO: Replace with your VAPID key from Firebase Console
serviceWorkerRegistration: registration,
});
console.log("[FCM] Token:", token);
return token;
} catch (err) {
console.error("[FCM] Error getting token:", err);
return null;
}
}
// Listen for foreground messages
export function onForegroundMessage(callback) {
if (!messaging) return () => {};
return onMessage(messaging, (payload) => {
console.log("[FCM] Foreground message:", payload);
callback(payload);
});
}
export { app, messaging };