File "useSyncFCMToken.ts"

Full Path: /home/trinadezambia/public_html/student_panel/src/lib/firebase/useSyncFCMToken.ts
File size: 7.13 KB
MIME-type: text/x-java
Charset: utf-8

/**
 * React Hook for Monitoring FCM Token Changes
 *
 * This hook monitors the FCM token and detects when it changes.
 * FCM tokens can change when:
 * - Service worker is re-registered
 * - User clears browser data
 * - Token expires or is refreshed by Firebase
 * - Browser/app is updated
 *
 * Since there's no separate update-token API endpoint, this hook:
 * - Monitors token changes
 * - Stores the latest token locally
 * - Logs warnings when token changes while logged in
 * - The new token will be sent on next login
 */

import { useEffect, useRef } from 'react';
import { useSelector } from 'react-redux';
import { initializeFirebase, getFCMToken } from './init';
import { RootState } from '@/components/store';

// Storage key for last known FCM token
const FCM_TOKEN_STORAGE_KEY = 'eschool_fcm_token';

/**
 * Hook to monitor FCM token changes
 *
 * Features:
 * - Automatically checks for token changes
 * - Stores latest token in localStorage
 * - Logs warnings when token changes while logged in
 * - Only runs when user is authenticated
 * - Prevents duplicate checks
 *
 * Note: Since there's no separate update-token API endpoint,
 * the new token will be sent to backend on next login.
 *
 * Usage: Call this hook once in your main dashboard layout
 */
export function useSyncFCMToken() {
  // Track if check is in progress to prevent duplicate calls
  const isCheckingRef = useRef(false);
  // Track if initial check is complete
  const hasInitialCheckRef = useRef(false);
  // Track if we've already shown warning for this session
  const hasShownWarningRef = useRef(false);

  // Get authentication state from Redux
  const { isAuthenticated, token: authToken } = useSelector(
    (state: RootState) => state.studentAuth
  );

  useEffect(() => {
    // Only monitor if user is authenticated
    if (!isAuthenticated || !authToken) {
      // console.log(
      //   '[FCM Monitor] User not authenticated, skipping FCM monitoring'
      // );
      return;
    }

    // Don't check if already in progress
    if (isCheckingRef.current) {
      // console.log('[FCM Monitor] Check already in progress, skipping');
      return;
    }

    // Function to check FCM token
    async function checkFCMToken() {
      try {
        isCheckingRef.current = true;
        // console.log('[FCM Monitor] Checking FCM token...');

        // Initialize Firebase if not already done
        await initializeFirebase();

        // Check if notifications are supported and permission is granted
        if (!('Notification' in window)) {
          // console.log(
          //   '[FCM Monitor] Notifications not supported in this browser'
          // );
          return;
        }

        if (Notification.permission !== 'granted') {
          // console.log('[FCM Monitor] Notification permission not granted');
          return;
        }

        // Get current FCM token from Firebase
        const currentToken = await getFCMToken();

        if (!currentToken) {
          // console.log('[FCM Monitor] No FCM token available');
          return;
        }

        // console.log(
        //   '[FCM Monitor] Current token:',
        //   currentToken.substring(0, 20) + '...'
        // );

        // Get stored token from localStorage (token sent during login)
        const storedToken = localStorage.getItem(FCM_TOKEN_STORAGE_KEY);
        // console.log(
        //   '[FCM Monitor] Token sent at login:',
        //   storedToken ? storedToken.substring(0, 20) + '...' : 'None'
        // );

        // Check if token has changed
        const hasTokenChanged = storedToken && currentToken !== storedToken;

        if (!storedToken) {
          // First time - store the current token
          // console.log('[FCM Monitor] Storing initial FCM token');
          localStorage.setItem(FCM_TOKEN_STORAGE_KEY, currentToken);
          hasInitialCheckRef.current = true;
        } else if (hasTokenChanged) {
          // Token changed while user is logged in!
          // console.warn(
          //   '⚠️ ═══════════════════════════════════════════════════════'
          // );
          // console.warn('[FCM Monitor] ⚠️ FCM TOKEN HAS CHANGED!');
          // console.warn(
          //   '[FCM Monitor] Old token:',
          //   storedToken.substring(0, 30) + '...'
          // );
          // console.warn(
          //   '[FCM Monitor] New token:',
          //   currentToken.substring(0, 30) + '...'
          // );
          // console.warn('[FCM Monitor]');
          // console.warn('[FCM Monitor] ⚠️ IMPORTANT: Backend has OLD token!');
          // console.warn(
          //   '[FCM Monitor] Notifications may NOT work until next login.'
          // );
          // console.warn('[FCM Monitor]');
          // console.warn(
          //   '[FCM Monitor] 💡 SOLUTION: User needs to logout and login again'
          // );
          // console.warn(
          //   '[FCM Monitor] The new token will be sent during login.'
          // );
          // console.warn(
          //   '⚠️ ═══════════════════════════════════════════════════════'
          // );

          // Store the new token for future comparison
          localStorage.setItem(FCM_TOKEN_STORAGE_KEY, currentToken);

          // Show user-friendly warning once per session
          if (!hasShownWarningRef.current) {
            hasShownWarningRef.current = true;
            // You could show a toast notification here
            // console.warn(
            //   '[FCM Monitor] 💡 TIP: If notifications stop working, please logout and login again.'
            // );
          }
        } else {
          // Token unchanged
          // console.log('[FCM Monitor] ✅ Token unchanged, all good!');
          hasInitialCheckRef.current = true;
        }
      } catch (error) {
        console.error('[FCM Monitor] ❌ Error checking token:', error);
      } finally {
        isCheckingRef.current = false;
      }
    }

    // Run initial check after a short delay
    // This gives time for authentication to fully settle
    const timeoutId = setTimeout(() => {
      checkFCMToken();
    }, 2000); // 2 second delay

    // Set up periodic check every 3 minutes
    // This catches any token refreshes that happen during the session
    const intervalId = setInterval(() => {
      if (!isCheckingRef.current) {
        // console.log('[FCM Monitor] Periodic token check...');
        checkFCMToken();
      }
    }, 3 * 60 * 1000); // 3 minutes

    // Cleanup function
    return () => {
      clearTimeout(timeoutId);
      clearInterval(intervalId);
    };
  }, [isAuthenticated, authToken]); // Re-run when auth state changes

  // This hook doesn't return anything
  // It just runs in the background to monitor token changes
}

/**
 * Clear stored FCM token
 *
 * Call this function when user logs out to clear the stored token.
 * This ensures a fresh token check on next login.
 */
export function clearStoredFCMToken() {
  localStorage.removeItem(FCM_TOKEN_STORAGE_KEY);
  // console.log('[FCM Sync] Stored token cleared');
}