add sync storage

This commit is contained in:
Fedor Katurov 2024-10-02 18:54:04 +07:00
parent b0665bfb2a
commit 6d00bffbec
14 changed files with 164 additions and 82 deletions

View file

@ -1,35 +1,26 @@
import { useEffect, useState } from "react";
const safelyGetStringValue = (key: string) => {
try {
return localStorage.getItem(key) ?? "";
} catch (error) {
console.warn(error);
return "";
}
};
const safelySetStringValue = (key: string, value: string) => {
try {
return localStorage.setItem(key, value);
} catch (error) {
console.warn(error);
}
};
import { useEffect, useMemo, useState } from "react";
import { BrowserSyncStorage } from '~/utils/index';
export const usePersistedValue = (
id: string,
prefix: string
): [string, (val: string) => void] => {
) => {
const [hydrated, setHydrated] = useState(false);
const storage = useMemo(() => new BrowserSyncStorage(prefix), [prefix]);
const key = `${prefix}${id}`;
const [value, setValue] = useState(safelyGetStringValue(key));
const [value, setValue] = useState('');
useEffect(() => {
setValue(safelyGetStringValue(key));
}, [id, key]);
storage.get<string>(key).then(val => setValue(val ?? '')).finally(() => setHydrated(true));
}, [key, storage]);
useEffect(() => {
safelySetStringValue(key, value);
}, [key, value]);
if (!hydrated) {
return;
}
return [value, setValue];
storage.set(key, value);
}, [key, value, storage, hydrated]);
return { value, setValue, hydrated };
};

View file

@ -30,7 +30,10 @@ export const MarkdownEditorContainer: FC<MarkdownEditorContainerProps> = ({
settings: { richEditorEnabled },
} = useSettings();
const [value, setValue] = usePersistedValue(id, "MarkdownEditorContainer");
const { value, setValue, hydrated } = usePersistedValue(
id,
"MarkdownEditorContainer"
);
const empty = !value.trim();
@ -52,7 +55,7 @@ export const MarkdownEditorContainer: FC<MarkdownEditorContainerProps> = ({
return (
<div className={styles.editor}>
<Suspense>{locked ? viewer : editor}</Suspense>
{hydrated && <Suspense>{locked ? viewer : editor}</Suspense>}
</div>
);
};

View file

@ -1,27 +1,30 @@
import { DockviewApi, DockviewReadyEvent } from "dockview";
import { useCallback, useEffect, useRef } from "react";
import { DockviewApi, DockviewReadyEvent, SerializedDockview } from "dockview";
import { useCallback, useEffect, useRef, useState } from "react";
import { createDefaultLayout } from "../utils/createDefaultLayout";
import { BrowserSyncStorage } from "~/utils";
const storage = new BrowserSyncStorage();
const key = 'dockview_persistance_layout';
export const useGridLayoutPersistance = () => {
const api = useRef<DockviewApi>();
const [hydrated, setHydrated] = useState(false);
const onReady = (event: DockviewReadyEvent) => {
api.current = event.api;
const layoutString = localStorage.getItem("dockview_persistance_layout");
storage.get<SerializedDockview>(key).then(layout => {
if (!layout) {
throw new Error("No layout saved, its okay");
}
if (!layoutString) {
createDefaultLayout(event.api);
return;
}
try {
const layout = JSON.parse(layoutString);
event.api.fromJSON(layout);
} catch (err) {
console.log(err);
}).catch(() => {
createDefaultLayout(event.api);
}
}).finally(() => {
setHydrated(true);
});
};
const persistLayout = useCallback(() => {
@ -29,18 +32,12 @@ export const useGridLayoutPersistance = () => {
return;
}
const layout = api.current.toJSON();
localStorage.setItem("dockview_persistance_layout", JSON.stringify(layout));
storage.set(key, api.current.toJSON());
}, []);
useEffect(() => {
if (!api.current) {
return;
}
const onLayoutChange = api.current.onDidLayoutChange(() => {
if (!api.current) {
const onLayoutChange = api.current?.onDidLayoutChange(() => {
if (!api.current || !hydrated) {
return;
}
@ -51,11 +48,8 @@ export const useGridLayoutPersistance = () => {
persistLayout();
});
return () => {
onLayoutChange.dispose();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [persistLayout, api.current]);
return onLayoutChange?.dispose;
}, [persistLayout, hydrated]);
return { api, onReady, persistLayout };
return { api, onReady, persistLayout, hydrated };
};

View file

@ -1,33 +1,37 @@
import { useCallback, useState } from "react";
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 getLocalStorageSettings = (defaultValue: SettingsValue) => {
try {
const raw = localStorage.getItem("settings");
const parsed = JSON.parse(raw ?? "");
return parsed ? { ...defaultValue, ...parsed } : defaultValue;
} catch (error) {
return defaultValue;
}
};
const storage = new BrowserSyncStorage('settings');
export const usePersistSettings = () => {
const [hydrated, setHydrated] = useState(false);
const defaultColors = useDefaultTheme();
const [settings, setSettings] = useState<SettingsValue>(
getLocalStorageSettings({ ...defaultSettings, ...defaultColors })
);
const [settings, setSettings] = useState({
...defaultSettings, ...defaultColors
});
const update = useCallback((val: Partial<SettingsValue>) => {
if (!hydrated) {
return;
}
setSettings((v) => {
const updatedValue = { ...v, ...val };
localStorage.setItem("settings", JSON.stringify(updatedValue));
storage.set("", updatedValue);
return updatedValue;
});
}, []);
}, [hydrated]);
return { settings, update };
useEffect(() => {
storage.get<SettingsValue>("").then(next => {
setSettings(prev => ({ ...prev, ...next }));
}).finally(() => setHydrated(true));
}, [])
return { settings, update, hydrated };
};

View file

@ -11,13 +11,17 @@ import styles from "./styles.module.scss";
const SettingsProvider: FC<PropsWithChildren> = ({ children }) => {
const { t } = useTranslation();
const { settings, update } = usePersistSettings();
const { settings, update, hydrated } = usePersistSettings();
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 && (

View file

@ -13,8 +13,6 @@ export const useThemeColors = (settings: ColorSettings) => {
const code = new Color(settings.codeColor);
console.log(code.hex());
// Backgrounds and text
document.body.style.setProperty("--color-background", background.hex());
document.body.style.setProperty("--color-code-text", code.hex());