web: add firebase push token registration and sync

This commit is contained in:
Codex
2026-03-09 23:12:40 +03:00
parent b1b54896a7
commit ef28c165e6
8 changed files with 1104 additions and 3 deletions

View File

@@ -0,0 +1,109 @@
import { initializeApp, getApps } from "firebase/app";
import { getMessaging, getToken, isSupported, onMessage, type MessagePayload } from "firebase/messaging";
import { deletePushToken, upsertPushToken } from "../api/notifications";
import { showNotificationViaServiceWorker } from "./webNotifications";
const WEB_PUSH_TOKEN_KEY = "bm_web_push_token";
let foregroundListenerAttached = false;
export async function ensureWebPushRegistration(): Promise<void> {
const config = getFirebaseConfig();
const vapidKey = import.meta.env.VITE_FIREBASE_VAPID_KEY?.trim();
if (!config || !vapidKey) {
return;
}
if (!(await isSupported())) {
return;
}
if (!("Notification" in window)) {
return;
}
if (Notification.permission === "default") {
await Notification.requestPermission();
}
if (Notification.permission !== "granted") {
return;
}
if (!("serviceWorker" in navigator)) {
return;
}
const registration = await navigator.serviceWorker.ready;
const app = getApps()[0] ?? initializeApp(config);
const messaging = getMessaging(app);
const token = await getToken(messaging, {
vapidKey,
serviceWorkerRegistration: registration,
});
if (!token) {
return;
}
const previous = window.localStorage.getItem(WEB_PUSH_TOKEN_KEY);
if (previous && previous !== token) {
await deletePushToken({ platform: "web", token: previous }).catch(() => undefined);
}
await upsertPushToken({ platform: "web", token, app_version: "web" });
window.localStorage.setItem(WEB_PUSH_TOKEN_KEY, token);
if (!foregroundListenerAttached) {
foregroundListenerAttached = true;
onMessage(messaging, (payload) => {
void showForegroundNotification(payload);
});
}
}
export async function unregisterWebPushToken(): Promise<void> {
const token = window.localStorage.getItem(WEB_PUSH_TOKEN_KEY);
if (!token) {
return;
}
await deletePushToken({ platform: "web", token }).catch(() => undefined);
window.localStorage.removeItem(WEB_PUSH_TOKEN_KEY);
}
async function showForegroundNotification(payload: MessagePayload): Promise<void> {
const data = payload.data ?? {};
const chatId = Number(data.chat_id);
const messageId = Number(data.message_id);
const title = payload.notification?.title ?? "New message";
const body = payload.notification?.body ?? "Open chat";
if (Number.isFinite(chatId) && Number.isFinite(messageId)) {
await showNotificationViaServiceWorker({
chatId,
messageId,
title,
body,
});
}
}
function getFirebaseConfig():
| {
apiKey: string;
authDomain?: string;
projectId: string;
storageBucket?: string;
messagingSenderId: string;
appId: string;
}
| null {
const apiKey = import.meta.env.VITE_FIREBASE_API_KEY?.trim();
const projectId = import.meta.env.VITE_FIREBASE_PROJECT_ID?.trim();
const messagingSenderId = import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID?.trim();
const appId = import.meta.env.VITE_FIREBASE_APP_ID?.trim();
if (!apiKey || !projectId || !messagingSenderId || !appId) {
return null;
}
return {
apiKey,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN?.trim() || undefined,
projectId,
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET?.trim() || undefined,
messagingSenderId,
appId,
};
}