{"version":3,"file":"use-media-permission.cjs","names":[],"sources":["../../src/hooks/use-media-permission.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\n\n/** Which capture permission to observe. */\nexport type MediaPermissionName = \"microphone\" | \"camera\";\n\n/**\n * Permission state, including the two cases the browser prompt cannot tell you\n * apart on its own.\n *\n * - `\"unknown\"` — the Permissions API is unavailable (Safari does not report\n *   `microphone`), so the only way to learn the answer is to ask for the device.\n * - `\"prompt\"` — never asked. Asking now shows the browser prompt.\n * - `\"granted\"` / `\"denied\"` — settled. `\"denied\"` is **sticky**: calling\n *   `getUserMedia` again does not re-prompt, it rejects immediately.\n */\nexport type MediaPermissionState = \"unknown\" | \"prompt\" | \"granted\" | \"denied\";\n\n/** Value returned by {@link useMediaPermission}. */\nexport interface UseMediaPermissionResult {\n    /** Current state. Updates live when the user changes it in browser settings. */\n    state: MediaPermissionState;\n    /** Whether the Permissions API could answer at all. */\n    supported: boolean;\n    /** Re-read the permission. Rarely needed — the hook subscribes to changes. */\n    refresh: () => void;\n}\n\n/**\n * Read a capture permission **without triggering the prompt**.\n *\n * This is the piece that makes a good permission flow possible. `getUserMedia` is\n * the only way to *get* a device, but calling it is also the only way to *ask*, and\n * a prompt fired on page load — before the user has pressed anything — is the\n * single most reliable way to earn a permanent \"Block\". Reading the state first\n * lets a page explain why it needs the microphone, and only then ask.\n *\n * It also separates \"never asked\" from \"denied\", which matters because they need\n * opposite UI: `\"prompt\"` gets a button, `\"denied\"` gets instructions for the\n * browser's site settings, since no amount of clicking will re-prompt.\n *\n * Safari does not expose `microphone` to the Permissions API and throws on the\n * query, which surfaces as `state: \"unknown\"` and `supported: false` — treat that\n * as \"you will have to ask to find out\", not as an error.\n *\n * @param name - Which permission to observe.\n * @returns The live state, whether it could be read, and a manual `refresh()`.\n *\n * @example\n * const { state } = useMediaPermission(\"microphone\");\n * if (state === \"denied\") return <p>Libere o microfone nas configurações do site.</p>;\n * return <button onClick={start}>Gravar</button>;\n */\nexport function useMediaPermission(name: MediaPermissionName): UseMediaPermissionResult {\n    const [state, setState] = useState<MediaPermissionState>(\"unknown\");\n    const [supported, setSupported] = useState(false);\n    const [token, setToken] = useState(0);\n\n    const refresh = useCallback(() => setToken((value) => value + 1), []);\n\n    useEffect(() => {\n        let cancelled = false;\n        let status: PermissionStatus | null = null;\n        const onChange = (): void => {\n            if (!cancelled && status) setState(status.state as MediaPermissionState);\n        };\n\n        async function read(): Promise<void> {\n            if (typeof navigator === \"undefined\" || !navigator.permissions?.query) {\n                if (!cancelled) {\n                    setSupported(false);\n                    setState(\"unknown\");\n                }\n                return;\n            }\n            try {\n                status = await navigator.permissions.query({\n                    name: name as PermissionName,\n                });\n                if (cancelled) return;\n                setSupported(true);\n                setState(status.state as MediaPermissionState);\n                status.addEventListener(\"change\", onChange);\n            } catch {\n                if (!cancelled) {\n                    setSupported(false);\n                    setState(\"unknown\");\n                }\n            }\n        }\n\n        void read();\n\n        return () => {\n            cancelled = true;\n            status?.removeEventListener(\"change\", onChange);\n        };\n    }, [name, token]);\n\n    return { state, supported, refresh };\n}\n"],"mappings":"uBAoDA,SAAgB,EAAmB,EAAqD,CACpF,GAAM,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAA+B,SAAS,EAC5D,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAS,EAAK,EAC1C,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAS,CAAC,EAE9B,GAAA,EAAU,EAAA,YAAA,KAAkB,EAAU,GAAU,EAAQ,CAAC,EAAG,CAAC,CAAC,EAyCpE,OAvCA,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAI,EAAY,GACZ,EAAkC,KAChC,MAAuB,CACrB,CAAC,GAAa,GAAQ,EAAS,EAAO,KAA6B,CAC3E,EAEA,eAAe,GAAsB,CACjC,GAAI,OAAO,UAAc,KAAe,CAAC,UAAU,aAAa,MAAO,CAC9D,IACD,EAAa,EAAK,EAClB,EAAS,SAAS,GAEtB,MACJ,CACA,GAAI,CAIA,GAHA,EAAS,MAAM,UAAU,YAAY,MAAM,CACjC,MACV,CAAC,EACG,EAAW,OACf,EAAa,EAAI,EACjB,EAAS,EAAO,KAA6B,EAC7C,EAAO,iBAAiB,SAAU,CAAQ,CAC9C,MAAQ,CACC,IACD,EAAa,EAAK,EAClB,EAAS,SAAS,EAE1B,CACJ,CAIA,OAFA,EAAU,MAEG,CACT,EAAY,GACZ,GAAQ,oBAAoB,SAAU,CAAQ,CAClD,CACJ,EAAG,CAAC,EAAM,CAAK,CAAC,EAET,CAAE,QAAO,YAAW,SAAQ,CACvC"}