/** * BoostMedia AI Content Generator Admin - Update Check Hook * * @package BoostMedia_AI * @license GPL-2.0-or-later */ import { useState, useEffect, useCallback } from 'react' const CACHE_KEY = 'bmai_update_check' const CHECK_INTERVAL_MS = 60 * 60 * 1000 // 1 hour const UPDATE_CLEARED_EVENT = 'bc:update-cache-cleared' let checkInFlight: Promise | null = null interface CachedUpdateStatus { updateAvailable: boolean latestVersion: string currentVersion: string downloadUrl: string changelog: string releaseDate: string lastChecked: number } export function useUpdateCheck() { const [updateAvailable, setUpdateAvailable] = useState(false) const [latestVersion, setLatestVersion] = useState(null) const [isChecking, setIsChecking] = useState(false) const rawApiUrl = window.bmaiSettings?.apiUrl || '/wp-json/bmai/v1/' const apiUrl = rawApiUrl.replace(/\/+$/, '') + '/' const nonce = window.bmaiSettings?.nonce || '' useEffect(() => { const handleCleared = () => { setUpdateAvailable(false) setLatestVersion(null) } window.addEventListener(UPDATE_CLEARED_EVENT, handleCleared) return () => window.removeEventListener(UPDATE_CLEARED_EVENT, handleCleared) }, []) useEffect(() => { const cached = getCachedStatus() if (cached) { setUpdateAvailable(cached.updateAvailable) setLatestVersion(cached.latestVersion) } if (shouldCheck(cached)) { checkForUpdates() } }, []) function getCachedStatus(): CachedUpdateStatus | null { try { const raw = localStorage.getItem(CACHE_KEY) if (!raw) return null return JSON.parse(raw) } catch { return null } } function shouldCheck(cached: CachedUpdateStatus | null): boolean { if (!cached) return true return Date.now() - cached.lastChecked > CHECK_INTERVAL_MS } const checkForUpdates = useCallback(async () => { if (checkInFlight) return checkInFlight setIsChecking(true) checkInFlight = (async () => { try { const response = await fetch(`${apiUrl}updates/check`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce }, credentials: 'same-origin', }) if (!response.ok) return const data = await response.json() const result = data.data || data const status: CachedUpdateStatus = { updateAvailable: !!result.update_available, latestVersion: result.latest_version || '', currentVersion: result.current_version || '', downloadUrl: result.download_url || '', changelog: result.changelog || '', releaseDate: result.release_date || '', lastChecked: Date.now(), } localStorage.setItem(CACHE_KEY, JSON.stringify(status)) setUpdateAvailable(status.updateAvailable) setLatestVersion(status.latestVersion) } catch { // Silently fail } finally { setIsChecking(false) checkInFlight = null } })() return checkInFlight }, [apiUrl, nonce]) const clearCache = useCallback(() => { localStorage.removeItem(CACHE_KEY) setUpdateAvailable(false) setLatestVersion(null) window.dispatchEvent(new Event(UPDATE_CLEARED_EVENT)) }, []) return { updateAvailable, latestVersion, isChecking, checkForUpdates, clearCache } }