import { Switch } from "../ui/atoms/switch"; import { getMDNWorker } from "./mdn-worker"; import useInterval from "@use-it/interval"; import { useEffect, useRef, useState } from "react"; import UpdateButton from "./update"; import ClearButton from "./clear"; import { Spinner } from "../ui/atoms/spinner"; import { MDN_PLUS_TITLE } from "../constants"; import { ContentStatus, ContentStatusPhase } from "./db"; import { OfflineSettingsData, useUserData } from "../user-context"; import { useLocale } from "../hooks"; import { TOGGLE_PLUS_OFFLINE_DISABLED, TOGGLE_PLUS_OFFLINE_ENABLED, } from "../telemetry/constants"; import { useGleanClick } from "../telemetry/glean-context"; function displayEstimate({ usage = 0, quota = Infinity }: StorageEstimate) { const usageInMib = Math.round(usage / (1024 * 1024)); return `${usageInMib} MiB`; } export default function OfflineSettings({ ...appProps }) { const serviceWorkerAvailable = window?.navigator?.serviceWorker; const user = useUserData(); const locale = useLocale(); return (

Offline Access

{user?.isSubscriber ? ( serviceWorkerAvailable ? ( ) : ( <>

Offline mode is unavailable

{" "}

Please make sure that you are not using a private or incognito window.

) ) : ( <> MDN Offline is only available to MDN Plus subscribers.{" "} Learn more about our plans. )}
); } function Settings() { document.title = `Settings | ${MDN_PLUS_TITLE}`; const [status, setStatus] = useState(); const [saving, setSaving] = useState(true); const [estimate, setEstimate] = useState(null); const [settings, setSettings] = useState(); // Workaround to avoid "Error: Too many re-renders." (https://github.com/mdn/yari/pull/5744). const updateTriggered = useRef(false); const gleanClick = useGleanClick(); useEffect(() => { const init = async () => { const mdnWorker = getMDNWorker(); setSettings(await mdnWorker.offlineSettings()); setEstimate(await navigator?.storage?.estimate?.()); mdnWorker.checkForUpdate(); }; init().then(() => {}); }, []); useEffect(() => { const init = async () => { setSaving(false); }; init(); }, [settings]); useEffect(() => { const mdnWorker = getMDNWorker(); const isWorkerBusy = status?.phase ? ![ContentStatusPhase.INITIAL, ContentStatusPhase.IDLE].includes( status?.phase ) : false; mdnWorker.toggleKeepAlive(isWorkerBusy); if (isWorkerBusy) { // Warn when leaving page. const listener = (e) => { e.preventDefault(); e.returnValue = ""; }; window.addEventListener("beforeunload", listener); return () => window.removeEventListener("beforeunload", listener); } }, [status?.phase]); const updateSettings = async (change: Partial) => { setSaving(true); const mdnWorker = getMDNWorker(); let newSettings = await mdnWorker.setOfflineSettings(change); setSettings(newSettings); }; useInterval(async () => { const mdnWorker = getMDNWorker(); const next = await mdnWorker.status(); setStatus({ ...next }); }, 500); const update = () => { const mdnWorker = getMDNWorker(); mdnWorker.update(); setStatus(status); }; const clear = async () => { if ( window.confirm("All downloaded content will be removed from your device") ) { const mdnWorker = getMDNWorker(); mdnWorker.clear(); setStatus(status); } }; if ( settings?.autoUpdates && status?.remote?.latest !== status?.local?.version && !updateTriggered.current ) { update(); updateTriggered.current = true; } const usage = estimate && displayEstimate(estimate); return (
  • MDN Offline Storage

    Enable storage to allow MDN content download for offline reading. {(saving === true && ) || ( { const source = e.target.checked ? TOGGLE_PLUS_OFFLINE_ENABLED : TOGGLE_PLUS_OFFLINE_DISABLED; gleanClick(source); updateSettings({ offline: e.target.checked, }); }} > )}
  • {settings?.offline && ( <>
  • Prefer online content

    Do not use offline content while connected to the internet {(saving === true && ) || ( updateSettings({ preferOnline: e.target.checked, }) } > )}
  • Enable auto-update

    Automatically download updates to content enabled for download {(saving === true && ) || ( updateSettings({ autoUpdates: e.target.checked, }) } > )}
  • {window?.location.hash === "#debug" && (
  • Debug

    {JSON.stringify(status, null, 2)}
  • )} {usage && (
  • Storage used

    MDN Offline currently uses {usage}
  • )}
  • )}
); }