| 1 | import { useEffect, useEffectEvent, useState } from "react"; |
| 2 | |
| 3 | export function usePermissionStatus<Id extends string>( |
| 4 | check: () => Promise<Record<Id, boolean>>, |
| 5 | initialStatus: Record<Id, boolean>, |
| 6 | enabled = true, |
| 7 | ) { |
| 8 | const [status, setStatus] = useState(initialStatus); |
| 9 | const [error, setError] = useState<string | null>(null); |
| 10 | const checkLatest = useEffectEvent(check); |
| 11 | useEffect(() => { |
| 12 | if (!enabled) return; |
| 13 | let disposed = false; |
| 14 | let checking = false; |
| 15 | const refresh = async () => { |
| 16 | if (disposed || checking || document.visibilityState === "hidden") return; |
| 17 | checking = true; |
| 18 | try { |
| 19 | const next = await checkLatest(); |
| 20 | if (!disposed) { |
| 21 | setStatus(next); |
| 22 | setError(null); |
| 23 | } |
| 24 | } catch { |
| 25 | if (!disposed) setError("Could not check permissions. We'll try again automatically."); |
| 26 | } |
| 27 | checking = false; |
| 28 | }; |
| 29 | void refresh(); |
| 30 | const timer = window.setInterval(() => void refresh(), 1500); |
| 31 | window.addEventListener("focus", refresh); |
| 32 | document.addEventListener("visibilitychange", refresh); |
| 33 | return () => { |
| 34 | disposed = true; |
| 35 | window.clearInterval(timer); |
| 36 | window.removeEventListener("focus", refresh); |
| 37 | document.removeEventListener("visibilitychange", refresh); |
| 38 | }; |
| 39 | }, [enabled]); |
| 40 | return { |
| 41 | status, |
| 42 | error, |
| 43 | isReady: (required: readonly Id[]) => error === null && required.every((id) => status[id]), |
| 44 | }; |
| 45 | } |