mirror of
https://github.com/muerwre/markdown-home-tab.git
synced 2025-04-25 00:46:41 +07:00
make global storage provider
This commit is contained in:
parent
2ba35b8e2b
commit
3a4e8e7702
9 changed files with 109 additions and 53 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "markdown-home-tab",
|
||||
"private": true,
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { createContext, useContext } from "react";
|
||||
import { noop } from "~/utils/noop";
|
||||
|
||||
export interface ColorSettings {
|
||||
backgroundColor: string;
|
||||
|
@ -26,10 +27,9 @@ export const defaultSettings: SettingsValue = {
|
|||
|
||||
export const SettingsContext = createContext({
|
||||
settings: defaultSettings,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
update: (_: Partial<SettingsValue>) => {},
|
||||
show: () => {},
|
||||
hide: () => {},
|
||||
update: noop as (value: Partial<SettingsValue>) => void,
|
||||
show: noop,
|
||||
hide: noop,
|
||||
});
|
||||
|
||||
export const useSettings = () => useContext(SettingsContext);
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import { SettingsValue } from "~/modules/settings/context/SettingsContext";
|
||||
import { useDefaultTheme } from "~/modules/theme/hooks/useDefaultTheme";
|
||||
import { BrowserSyncStorage } from "~/utils";
|
||||
import { defaultSettings } from "../context/SettingsContext";
|
||||
|
||||
const storage = new BrowserSyncStorage('settings');
|
||||
|
||||
export const usePersistSettings = () => {
|
||||
const [hydrated, setHydrated] = useState(false);
|
||||
|
||||
const defaultColors = useDefaultTheme();
|
||||
|
||||
const [settings, setSettings] = useState({
|
||||
...defaultSettings, ...defaultColors
|
||||
});
|
||||
|
||||
const update = useCallback((val: Partial<SettingsValue>) => {
|
||||
if (!hydrated) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSettings((v) => {
|
||||
const updatedValue = { ...v, ...val };
|
||||
storage.set("", updatedValue);
|
||||
return updatedValue;
|
||||
});
|
||||
}, [hydrated]);
|
||||
|
||||
useEffect(() => {
|
||||
storage.get<SettingsValue>("").then(next => {
|
||||
setSettings(prev => ({ ...prev, ...next }));
|
||||
}).finally(() => setHydrated(true));
|
||||
}, [])
|
||||
|
||||
return { settings, update, hydrated };
|
||||
};
|
17
src/modules/settings/hooks/useSettings.ts
Normal file
17
src/modules/settings/hooks/useSettings.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { useMemo } from "react";
|
||||
import { useStorage } from "~/modules/storage/StorageContext";
|
||||
import { useDefaultTheme } from "~/modules/theme/hooks/useDefaultTheme";
|
||||
import { defaultSettings } from "../context/SettingsContext";
|
||||
|
||||
export const useSettings = () => {
|
||||
const defaultColors = useDefaultTheme();
|
||||
|
||||
const { settings: storedSettings, setSettings } = useStorage();
|
||||
|
||||
const settings = useMemo(
|
||||
() => ({ ...defaultSettings, ...defaultColors, ...storedSettings }),
|
||||
[defaultColors, storedSettings]
|
||||
);
|
||||
|
||||
return { settings, update: setSettings };
|
||||
};
|
|
@ -5,23 +5,19 @@ import { ModalPage } from "~/modules/modal/components/ModalPage";
|
|||
import { Modal } from "~/modules/modal/containers/Modal";
|
||||
import { SettingsContainer } from "../../containers/SettingsContainer";
|
||||
import { SettingsContext } from "../../context/SettingsContext";
|
||||
import { usePersistSettings } from "../../hooks/usePersistSettings";
|
||||
import { useSettings } from "../../hooks/useSettings";
|
||||
import styles from "./styles.module.scss";
|
||||
|
||||
const SettingsProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { settings, update, hydrated } = usePersistSettings();
|
||||
const { settings, update } = useSettings();
|
||||
|
||||
const [settingsModalVisible, setSettingsModalVisible] = useState(false);
|
||||
|
||||
const show = useCallback(() => setSettingsModalVisible(true), []);
|
||||
const hide = useCallback(() => setSettingsModalVisible(false), []);
|
||||
|
||||
if (!hydrated) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsContext.Provider value={{ settings, update, show, hide }}>
|
||||
{settingsModalVisible && (
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import { createContext, useContext } from "react";
|
||||
import { SerializedDockview } from "dockview";
|
||||
import { noop } from "~/utils/noop";
|
||||
import { SettingsValue } from "~/modules/settings/context/SettingsContext";
|
||||
|
||||
export const StorageContext = createContext({
|
||||
layout: null as SerializedDockview | null,
|
||||
panels: {} as Record<string, string>,
|
||||
settings: {} as Partial<SettingsValue>,
|
||||
hydrated: false,
|
||||
setPanel: noop as (uuid: string, content: string) => void,
|
||||
setLayout: noop as (layout: SerializedDockview) => void,
|
||||
setSettings: noop as (layout: Partial<SettingsValue>) => void,
|
||||
});
|
||||
|
||||
export const useStorage = () => useContext(StorageContext);
|
||||
|
|
|
@ -4,9 +4,11 @@ import {
|
|||
hydrateLayout,
|
||||
storeLayoutLocally,
|
||||
storePanelLocally,
|
||||
storeSettingsLocally,
|
||||
} from "~/utils/hydrate";
|
||||
import { useDelayedSync } from "./hooks/useDelayedSync";
|
||||
import { StorageContext } from "./StorageContext";
|
||||
import { SettingsValue } from "~/modules/settings/context/SettingsContext";
|
||||
|
||||
const debounceDelay = 500;
|
||||
|
||||
|
@ -14,8 +16,10 @@ export const StorageProvider = ({ children }: { children: ReactNode }) => {
|
|||
const [hydrated, setHydrated] = useState(false);
|
||||
const [layout, setLayoutValue] = useState<SerializedDockview | null>(null);
|
||||
const [panels, setPanelsValue] = useState<Record<string, string>>({});
|
||||
const [settings, setSettingsValue] = useState<Partial<SettingsValue>>({});
|
||||
|
||||
const { storeLayout, storePanel } = useDelayedSync(debounceDelay);
|
||||
const { storeLayout, storePanel, storeSettings } =
|
||||
useDelayedSync(debounceDelay);
|
||||
|
||||
const setPanel = useCallback(
|
||||
(uuid: string, value: string) => {
|
||||
|
@ -35,6 +39,12 @@ export const StorageProvider = ({ children }: { children: ReactNode }) => {
|
|||
[storeLayout]
|
||||
);
|
||||
|
||||
const setSettings = useCallback((value: Partial<SettingsValue>) => {
|
||||
setSettingsValue(value);
|
||||
storeSettingsLocally(value);
|
||||
storeSettings(value);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (hydrated) {
|
||||
return;
|
||||
|
@ -47,6 +57,7 @@ export const StorageProvider = ({ children }: { children: ReactNode }) => {
|
|||
}
|
||||
|
||||
setLayout(result.layout);
|
||||
setSettings(result.settings);
|
||||
|
||||
Object.entries(result.panels).forEach(([uuid, value]) => {
|
||||
setPanel(uuid, value);
|
||||
|
@ -55,11 +66,19 @@ export const StorageProvider = ({ children }: { children: ReactNode }) => {
|
|||
.finally(() => {
|
||||
setHydrated(true);
|
||||
});
|
||||
}, [hydrated, setLayout, setPanel]);
|
||||
}, [hydrated, setLayout, setPanel, setSettings]);
|
||||
|
||||
return (
|
||||
<StorageContext.Provider
|
||||
value={{ hydrated, layout, panels, setLayout, setPanel }}
|
||||
value={{
|
||||
hydrated,
|
||||
layout,
|
||||
panels,
|
||||
settings,
|
||||
setLayout,
|
||||
setPanel,
|
||||
setSettings,
|
||||
}}
|
||||
>
|
||||
{hydrated ? children : null}
|
||||
</StorageContext.Provider>
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import { useCallback, useRef } from "react";
|
||||
import { storeLayoutInSync, storePanelInSync } from "~/utils/hydrate";
|
||||
import {
|
||||
storeLayoutInSync,
|
||||
storePanelInSync,
|
||||
storeSettingsInSync,
|
||||
} from "~/utils/hydrate";
|
||||
import { DebouncedFunc } from "lodash";
|
||||
import { SerializedDockview } from "dockview";
|
||||
import debounce from "lodash.debounce";
|
||||
import { SettingsValue } from "~/modules/settings/context/SettingsContext";
|
||||
|
||||
export const useDelayedSync = (debounceDelay: number) => {
|
||||
const layoutTimer = useRef<DebouncedFunc<typeof storeLayoutInSync>>();
|
||||
const settingsTimer = useRef<DebouncedFunc<typeof storeSettingsInSync>>();
|
||||
const panelTimers = useRef<
|
||||
Record<string, DebouncedFunc<typeof storePanelInSync>>
|
||||
>({});
|
||||
|
@ -34,5 +40,16 @@ export const useDelayedSync = (debounceDelay: number) => {
|
|||
[debounceDelay]
|
||||
);
|
||||
|
||||
return { storeLayout, storePanel };
|
||||
const storeSettings = useCallback(
|
||||
(settings: Partial<SettingsValue>) => {
|
||||
if (settingsTimer.current) {
|
||||
settingsTimer.current.cancel();
|
||||
}
|
||||
|
||||
settingsTimer.current = debounce(storeSettingsInSync, debounceDelay);
|
||||
settingsTimer.current(settings);
|
||||
},
|
||||
[debounceDelay]
|
||||
);
|
||||
return { storeLayout, storePanel, storeSettings };
|
||||
};
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { SerializedDockview } from "dockview";
|
||||
import { SettingsValue } from "~/modules/settings/context/SettingsContext";
|
||||
import { hasBrowserStorage, hasChromeStorage } from "~/utils/storage";
|
||||
|
||||
interface Result {
|
||||
layout: SerializedDockview;
|
||||
panels: Record<string, string>;
|
||||
settings: Partial<SettingsValue>;
|
||||
}
|
||||
|
||||
const layoutKey = "dockview_persistance_layout";
|
||||
const settingsKey = "settings";
|
||||
const panelPrefix = "MarkdownEditorContainerMarkdownEditorContainer";
|
||||
|
||||
const makePanelKey = (uuid: string) => `${panelPrefix}${uuid}`;
|
||||
|
@ -27,9 +30,16 @@ const getFromBrowserStorage = async (): Promise<Result | null> => {
|
|||
{} as Record<string, string>
|
||||
);
|
||||
|
||||
const settings =
|
||||
typeof result[settingsKey] === "object" &&
|
||||
Object.keys(result[settingsKey]).length
|
||||
? result[settingsKey]
|
||||
: {};
|
||||
|
||||
return {
|
||||
layout,
|
||||
panels,
|
||||
settings,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -49,9 +59,16 @@ const getFromChromeStorage = async (): Promise<Result | null> => {
|
|||
{} as Record<string, string>
|
||||
);
|
||||
|
||||
const settings =
|
||||
typeof result[settingsKey] === "object" &&
|
||||
Object.keys(result[settingsKey]).length
|
||||
? result[settingsKey]
|
||||
: {};
|
||||
|
||||
return {
|
||||
layout,
|
||||
panels,
|
||||
settings,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -76,9 +93,20 @@ const getFromLocalStorage = () => {
|
|||
{} as Record<string, string>
|
||||
);
|
||||
|
||||
const rawSettings = localStorage.getItem(settingsKey);
|
||||
const parsedSettings =
|
||||
rawSettings && (JSON.parse(rawSettings) as Partial<SettingsValue>);
|
||||
const settings =
|
||||
parsedSettings &&
|
||||
typeof parsedSettings === "object" &&
|
||||
Object.keys(parsedSettings).length
|
||||
? parsedSettings
|
||||
: {};
|
||||
|
||||
return {
|
||||
layout,
|
||||
panels,
|
||||
settings,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -125,3 +153,16 @@ export const storePanelInSync = (uuid: string, value: string) => {
|
|||
return chrome.storage.sync.set({ [`${panelPrefix}${uuid}`]: value });
|
||||
}
|
||||
};
|
||||
|
||||
export const storeSettingsLocally = (settings: Partial<SettingsValue>) =>
|
||||
localStorage.setItem(settingsKey, JSON.stringify(settings));
|
||||
|
||||
export const storeSettingsInSync = (settings: Partial<SettingsValue>) => {
|
||||
if (hasBrowserStorage()) {
|
||||
return browser.storage.sync.set({ [settingsKey]: settings });
|
||||
}
|
||||
|
||||
if (hasChromeStorage()) {
|
||||
return chrome.storage.sync.set({ [settingsKey]: settings });
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue