logo

NextjsでFCM使ってみた

2024-06-27
5 months ago

開発環境

  • next 14.2.4
  • firebase 10.12.2

前提

Nextjs、Firebaseのプロジェクトは作成済みを想定しています。

開発環境でフォアグラウンド、バックグランドで通知を受け取るところまでをゴールにしています。

また、firebaseConfigなど環境変数を入れるところは<your-xxx>としていますので、適宜ご自身の環境で設定してください。

本題

仕事でNextjsのプロジェクトにプッシュ通知を導入する予定があり、試しで簡単なサンプルを実装してみます。

1. firebase SDK導入

$ yarn add firebase
$ touch src/lib/firebase.ts
// Import the functions you need from the SDKs you need
import { initializeApp } from 'firebase/app';
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: '<your-api-key>',
  authDomain: '<your-auth-domain>',
  projectId: '<your-project-id>',
  storageBucket: '<your-storage-bucket>',
  messagingSenderId: '<your-messaging-sender-id>',
  appId: '<your-app-id>',
};

// Initialize Firebase
const firebaseApp = initializeApp(firebaseConfig);
export default firebaseApp;
src/lib/firebase.ts

2. サービスワーカー設定

$ touch public/firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/8.8.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.8.0/firebase-messaging.js');

const firebaseConfig = {
  apiKey: '<your-api-key>',
  authDomain: '<your-auth-domain>',
  projectId: '<your-project-id>',
  storageBucket: '<your-storage-bucket>',
  messagingSenderId: '<your-messaging-sender-id>',
  appId: '<your-app-id>',
};
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) => {
  console.log(
    '[firebase-messaging-sw.js] Received background message ',
    payload,
  );
  const notificationTitle = payload.notification.title;
  const notificationOptions = {
    body: payload.notification.body,
    icon: './your-logo.png',
  };
  self.registration.showNotification(notificationTitle, notificationOptions);
});
public/firebase-messaging-sw.js

3. ユーザーに通知を許可してもらうためのHooks

$ touch src/hooks/use-fcm-token.ts
import { useEffect, useState } from 'react';

import { getMessaging, getToken } from 'firebase/messaging';

import firebaseApp from '@/lib/firebase';

export const useFcmToken = () => {
  const [token, setToken] = useState('');
  const [notificationPermissionStatus, setNotificationPermissionStatus] =
    useState('');

  useEffect(() => {
    const retrieveToken = async () => {
      try {
        if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
          const messaging = getMessaging(firebaseApp);

          // Retrieve the notification permission status
          const permission = await Notification.requestPermission();
          setNotificationPermissionStatus(permission);

          // Check if permission is granted before retrieving the token
          if (permission === 'granted') {
            const currentToken = await getToken(messaging, {
              vapidKey: '<your-vapid-key>',
            });
            if (currentToken) {
              setToken(currentToken);
            } else {
              console.log(
                'No registration token available. Request permission to generate one.',
              );
            }
          }
        }
      } catch (error) {
        console.log('An error occurred while retrieving token:', error);
      }
    };

    retrieveToken();
  }, []);

  return { fcmToken: token, notificationPermissionStatus };
};
src/hooks/use-fcm-token.ts

4. HooksをProviderに配置

$ touch src/providers/fcm.tsx
$ touch src/providers/app.tsx
import { type ReactNode, useEffect } from 'react';

import { getMessaging, onMessage } from 'firebase/messaging';

import { useFcmToken } from '@/hooks/use-fcm-token';
import firebaseApp from '@/lib/firebase';

type FCMProviderProps = {
  children: ReactNode;
};

export const FCMProvider = ({ children }: FCMProviderProps) => {
  const { fcmToken, notificationPermissionStatus } = useFcmToken();
  // Use the token and status as needed
  console.log({ fcmToken });
  console.log({ notificationPermissionStatus });

  useEffect(() => {
    if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
      const messaging = getMessaging(firebaseApp);
      const unsubscribe = onMessage(messaging, (payload) => {
        console.log('Foreground push notification received:', payload);
        // Handle the received push notification while the app is in the foreground
        // You can display a notification or update the UI based on the payload
      });
      return () => {
        unsubscribe(); // Unsubscribe from the onMessage event
      };
    }
  }, []);

  return <>{children}</>;
};
src/providers/fcm.tsx
'use client';

import React, { type ReactNode } from 'react';

import { FCMProvider } from './fcm';

type AppProviderProps = {
  children: ReactNode;
};

export const AppProvider = ({ children }: AppProviderProps) => {
  return <FCMProvider>{children}</FCMProvider>;
};
src/providers/app.tsx


AppProviderをrootのlayout.tsxでimportして利用してください。

注意点

バックグラウンド通知の動作確認する上で、OSの通知設定(ブラウザの通知設定)を「オン」にする必要があります。

オフのままだとメッセージは受け取れているが、通知が届かない、という状態になるのでご注意ください。

さいごに

firebaseの説明を割愛しましたが、firebaseコンソールから適当にプロジェクトを作成し、Cloud Messagingにて「新しいキャンペーンの作成」でメッセージを送信することができます。

参照