/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React, { useContext, useEffect, useMemo, useState, useCallback, } from 'react';
import { useAllDocsData, useDocsData, } from '@docusaurus/plugin-content-docs/client';
import { DEFAULT_PLUGIN_ID } from '@docusaurus/constants';
import { useThemeConfig, } from '../utils/useThemeConfig';
import { isDocsPluginEnabled } from '../utils/docsUtils';
import { ReactContextError } from '../utils/reactUtils';
import { createStorageSlot } from '../utils/storageUtils';
const storageKey = (pluginId) => `docs-preferred-version-${pluginId}`;
const DocsPreferredVersionStorage = {
save: (pluginId, persistence, versionName) => {
createStorageSlot(storageKey(pluginId), { persistence }).set(versionName);
},
read: (pluginId, persistence) => createStorageSlot(storageKey(pluginId), { persistence }).get(),
clear: (pluginId, persistence) => {
createStorageSlot(storageKey(pluginId), { persistence }).del();
},
};
/**
* Initial state is always null as we can't read local storage from node SSR
*/
const getInitialState = (pluginIds) => Object.fromEntries(pluginIds.map((id) => [id, { preferredVersionName: null }]));
/**
* Read storage for all docs plugins, assigning each doc plugin a preferred
* version (if found)
*/
function readStorageState({ pluginIds, versionPersistence, allDocsData, }) {
/**
* The storage value we read might be stale, and belong to a version that does
* not exist in the site anymore. In such case, we remove the storage value to
* avoid downstream errors.
*/
function restorePluginState(pluginId) {
const preferredVersionNameUnsafe = DocsPreferredVersionStorage.read(pluginId, versionPersistence);
const pluginData = allDocsData[pluginId];
const versionExists = pluginData.versions.some((version) => version.name === preferredVersionNameUnsafe);
if (versionExists) {
return { preferredVersionName: preferredVersionNameUnsafe };
}
DocsPreferredVersionStorage.clear(pluginId, versionPersistence);
return { preferredVersionName: null };
}
return Object.fromEntries(pluginIds.map((id) => [id, restorePluginState(id)]));
}
function useVersionPersistence() {
return useThemeConfig().docs.versionPersistence;
}
const Context = React.createContext(null);
function useContextValue() {
const allDocsData = useAllDocsData();
const versionPersistence = useVersionPersistence();
const pluginIds = useMemo(() => Object.keys(allDocsData), [allDocsData]);
// Initial state is empty, as we can't read browser storage in node/SSR
const [state, setState] = useState(() => getInitialState(pluginIds));
// On mount, we set the state read from browser storage
useEffect(() => {
setState(readStorageState({ allDocsData, versionPersistence, pluginIds }));
}, [allDocsData, versionPersistence, pluginIds]);
// The API that we expose to consumer hooks (memo for constant object)
const api = useMemo(() => {
function savePreferredVersion(pluginId, versionName) {
DocsPreferredVersionStorage.save(pluginId, versionPersistence, versionName);
setState((s) => ({
...s,
[pluginId]: { preferredVersionName: versionName },
}));
}
return {
savePreferredVersion,
};
}, [versionPersistence]);
return [state, api];
}
function DocsPreferredVersionContextProviderUnsafe({ children, }) {
const value = useContextValue();
return {children};
}
/**
* This is a maybe-layer. If the docs plugin is not enabled, this provider is a
* simple pass-through.
*/
export function DocsPreferredVersionContextProvider({ children, }) {
if (isDocsPluginEnabled) {
return (
{children}
);
}
return <>{children}>;
}
function useDocsPreferredVersionContext() {
const value = useContext(Context);
if (!value) {
throw new ReactContextError('DocsPreferredVersionContextProvider');
}
return value;
}
/**
* Returns a read-write interface to a plugin's preferred version. The
* "preferred version" is defined as the last version that the user visited.
* For example, if a user is using v3, even when v4 is later published, the user
* would still be browsing v3 docs when she opens the website next time. Note,
* the `preferredVersion` attribute will always be `null` before mount.
*/
export function useDocsPreferredVersion(pluginId = DEFAULT_PLUGIN_ID) {
const docsData = useDocsData(pluginId);
const [state, api] = useDocsPreferredVersionContext();
const { preferredVersionName } = state[pluginId];
const preferredVersion = docsData.versions.find((version) => version.name === preferredVersionName) ?? null;
const savePreferredVersionName = useCallback((versionName) => {
api.savePreferredVersion(pluginId, versionName);
}, [api, pluginId]);
return { preferredVersion, savePreferredVersionName };
}
export function useDocsPreferredVersionByPluginId() {
const allDocsData = useAllDocsData();
const [state] = useDocsPreferredVersionContext();
function getPluginIdPreferredVersion(pluginId) {
const docsData = allDocsData[pluginId];
const { preferredVersionName } = state[pluginId];
return (docsData.versions.find((version) => version.name === preferredVersionName) ?? null);
}
const pluginIds = Object.keys(allDocsData);
return Object.fromEntries(pluginIds.map((id) => [id, getPluginIdPreferredVersion(id)]));
}
//# sourceMappingURL=docsPreferredVersion.js.map