mirror of
https://github.com/Vomitblood/stort.git
synced 2024-11-26 13:55:27 +08:00
settings json functions
This commit is contained in:
parent
6e0c1d2b85
commit
50e8016b14
|
@ -19,6 +19,7 @@
|
|||
"@tauri-apps/api": "^1.6.0",
|
||||
"@types/lodash": "^4.17.7",
|
||||
"lodash": "^4.17.21",
|
||||
"lowdb": "^7.0.1",
|
||||
"next": "14.2.5",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
|
|
|
@ -1,24 +1,19 @@
|
|||
import { merge } from "lodash";
|
||||
import {
|
||||
createContext,
|
||||
FC,
|
||||
ReactNode,
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { defaultSettings, SettingsType } from "../lib/defaultSettings";
|
||||
import { logcat } from "../lib/logcatService";
|
||||
import { LowDB } from "../lib/lowDB";
|
||||
|
||||
// settings context
|
||||
type SettingsContextProps = {
|
||||
settings: SettingsType;
|
||||
fetchUserSettingsLocal: () => Promise<void>;
|
||||
fetchUserSettingsCloud: () => Promise<void>;
|
||||
updateSettingsLocal: (settings: SettingsType) => void;
|
||||
updateSettingsCloud: (settings: SettingsType) => Promise<void>;
|
||||
deleteSettingsLocal: () => void;
|
||||
deleteSettingsCloud: () => Promise<void>;
|
||||
settingsLoading: boolean;
|
||||
};
|
||||
|
||||
|
@ -33,163 +28,46 @@ export const SettingsProvider: FC<{ children: ReactNode }> = ({ children }) => {
|
|||
const [settings, setSettings] = useState<SettingsType>(defaultSettings);
|
||||
const [settingsLoading, setSettingsLoading] = useState<boolean>(true);
|
||||
|
||||
// fetch user settings from localStorage
|
||||
const fetchUserSettingsLocal = async () => {
|
||||
// initialize a new lowdb instance for settings outside of state
|
||||
const settingsDB = useMemo(() => {
|
||||
return new LowDB<SettingsType>("settings.json", defaultSettings);
|
||||
}, []); // empty dependency array ensures this is only created once
|
||||
|
||||
// function to update settings
|
||||
const updateSettings = async (updates: Partial<SettingsType>) => {
|
||||
try {
|
||||
logcat.log("Fetching user settings from localStorage...", "INFO");
|
||||
const userSettings = localStorage.getItem("userSettings");
|
||||
|
||||
if (userSettings) {
|
||||
// deep merge user settings with default settings
|
||||
const mergedSettings = merge(
|
||||
{},
|
||||
defaultSettings,
|
||||
JSON.parse(userSettings),
|
||||
);
|
||||
|
||||
setSettings(mergedSettings);
|
||||
|
||||
logcat.log("User settings fetched from localStorage", "INFO");
|
||||
logcat.log("User settings: " + userSettings, "DEBUG");
|
||||
} else {
|
||||
logcat.log(
|
||||
"User settings not found in localStorage, using default settings",
|
||||
"WARN",
|
||||
);
|
||||
}
|
||||
setSettingsLoading(false);
|
||||
await settingsDB.updateData((data) => {
|
||||
Object.assign(data, updates);
|
||||
});
|
||||
const newSettings = await settingsDB.readData();
|
||||
setSettings(newSettings);
|
||||
logcat.log("Settings updated successfully", "INFO");
|
||||
} catch (error) {
|
||||
logcat.log("Error fetching user settings from localStorage", "ERROR");
|
||||
logcat.log(String(error), "ERROR");
|
||||
setSettingsLoading(false);
|
||||
logcat.log(`Failed to update settings: ${error}`, "ERROR");
|
||||
}
|
||||
};
|
||||
|
||||
// fetch user settings from Firestore
|
||||
const fetchUserSettingsCloud = async () => {
|
||||
try {
|
||||
if (user) {
|
||||
logcat.log("Fetching user settings from firestore...", "INFO");
|
||||
const userSettings = await firestoreRetrieveDocumentField(
|
||||
`users/${user.uid}`,
|
||||
"settings",
|
||||
);
|
||||
|
||||
if (userSettings) {
|
||||
// deep merge user settings with default settings
|
||||
const mergedSettings = merge({}, defaultSettings, userSettings);
|
||||
|
||||
setSettings(mergedSettings);
|
||||
|
||||
logcat.log("User settings fetched from firestore", "INFO");
|
||||
logcat.log("User settings: " + JSON.stringify(userSettings), "DEBUG");
|
||||
|
||||
// and then save to localStorage
|
||||
updateSettingsLocal(mergedSettings);
|
||||
} else {
|
||||
logcat.log(
|
||||
"User settings not found in firestore, using default settings",
|
||||
"WARN",
|
||||
);
|
||||
}
|
||||
setSettingsLoading(false);
|
||||
} else {
|
||||
logcat.log("User not logged in, using default settings", "WARN");
|
||||
setSettingsLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
logcat.log("No such field", "ERROR");
|
||||
logcat.log(String(error), "ERROR");
|
||||
setSettingsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// push new settings to localStorage
|
||||
const updateSettingsLocal = (settings: SettingsType) => {
|
||||
try {
|
||||
// apply settings into state
|
||||
logcat.log("Applying settings...", "INFO");
|
||||
setSettings(settings);
|
||||
logcat.log("Settings applied", "INFO");
|
||||
|
||||
// save settings to localStorage
|
||||
logcat.log("Saving user settings to localStorage...", "INFO");
|
||||
localStorage.setItem("userSettings", JSON.stringify(settings));
|
||||
logcat.log("User settings saved to localStorage", "INFO");
|
||||
} catch (error) {
|
||||
logcat.log("Error saving user settings to localStorage", "ERROR");
|
||||
logcat.log(String(error), "ERROR");
|
||||
}
|
||||
};
|
||||
|
||||
// push new settings to firestore
|
||||
const updateSettingsCloud = async (settings: SettingsType) => {
|
||||
try {
|
||||
// apply settings into state
|
||||
logcat.log("Applying settings...", "INFO");
|
||||
setSettings(settings);
|
||||
logcat.log("Settings applied", "INFO");
|
||||
|
||||
// save settings to firestore
|
||||
logcat.log("Saving user settings to Firestore...", "INFO");
|
||||
|
||||
// push entire settings object to firestore
|
||||
if (user) {
|
||||
firestoreCommitUpdate(`users/${user.uid}`, "settings", settings);
|
||||
await firestorePushUpdates();
|
||||
|
||||
logcat.log("User settings saved to firestore", "INFO");
|
||||
}
|
||||
} catch (error) {
|
||||
logcat.log("Error saving user settings to firestore", "ERROR");
|
||||
logcat.log(String(error), "ERROR");
|
||||
}
|
||||
};
|
||||
|
||||
// delete settings from localStorage
|
||||
const deleteSettingsLocal = () => {
|
||||
try {
|
||||
logcat.log("Deleting user settings from localStorage...", "INFO");
|
||||
localStorage.removeItem("userSettings");
|
||||
logcat.log("User settings deleted from localStorage", "INFO");
|
||||
} catch (error) {
|
||||
logcat.log("Error deleting user settings from localStorage", "ERROR");
|
||||
logcat.log(String(error), "ERROR");
|
||||
}
|
||||
};
|
||||
|
||||
// delete settings from cloud
|
||||
const deleteSettingsCloud = async () => {
|
||||
try {
|
||||
logcat.log("Deleting user settings from Firestore...", "INFO");
|
||||
|
||||
if (user) {
|
||||
firestoreCommitDelete(`users/${user.uid}`, "settings");
|
||||
await firestorePushDeletes();
|
||||
|
||||
logcat.log("User settings deleted from Firestore", "INFO");
|
||||
}
|
||||
} catch (error) {
|
||||
logcat.log(String(error), "ERROR");
|
||||
throw new Error("Error deleting user settings from Firestore");
|
||||
}
|
||||
};
|
||||
|
||||
// fetch user settings from local on first load everytime
|
||||
// fetch user settings from local on first load every time
|
||||
useEffect(() => {
|
||||
fetchUserSettingsLocal();
|
||||
}, []);
|
||||
const fetchSettings = async () => {
|
||||
try {
|
||||
await settingsDB.init();
|
||||
const storedSettings = await settingsDB.readData();
|
||||
setSettings(storedSettings);
|
||||
} catch (error) {
|
||||
logcat.log(`Failed to load settings: ${error}`, "ERROR");
|
||||
} finally {
|
||||
setSettingsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchSettings();
|
||||
}, [settingsDB]);
|
||||
|
||||
return (
|
||||
<SettingsContext.Provider
|
||||
value={{
|
||||
settings,
|
||||
fetchUserSettingsLocal,
|
||||
fetchUserSettingsCloud,
|
||||
updateSettingsLocal,
|
||||
updateSettingsCloud,
|
||||
deleteSettingsLocal,
|
||||
deleteSettingsCloud,
|
||||
settingsLoading,
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -9,16 +9,6 @@ export const defaultSettings = {
|
|||
font_family: "monospace" as string,
|
||||
font_scaling: 100,
|
||||
},
|
||||
timings: {
|
||||
auto_refresh: false as boolean,
|
||||
auto_refresh_interval: 30 as number,
|
||||
eta_as_duration: true as boolean,
|
||||
military_time_format: true as boolean,
|
||||
timings_bus_expanded_items: 3 as number,
|
||||
timings_train_expanded_items: 3 as number,
|
||||
favourites_bus_expanded_items: 0 as number,
|
||||
favourites_train_expanded_items: 0 as number,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type SettingsType = typeof defaultSettings;
|
||||
|
|
53
src/lib/lowDB.ts
Normal file
53
src/lib/lowDB.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { merge } from "lodash";
|
||||
import { Low } from "lowdb";
|
||||
import { JSONFile } from "lowdb/node";
|
||||
|
||||
// define a generic interface for json structure
|
||||
interface Database<T> {
|
||||
data: T;
|
||||
}
|
||||
|
||||
type UpdateCallback<T> = (data: T) => void;
|
||||
|
||||
export class LowDB<T> {
|
||||
private db: Low<Database<T>>;
|
||||
private defaultData: T;
|
||||
|
||||
constructor(filePath: string, defaultData: T) {
|
||||
const adapter = new JSONFile<Database<T>>(filePath);
|
||||
this.db = new Low(adapter, { data: defaultData });
|
||||
this.defaultData = defaultData;
|
||||
}
|
||||
|
||||
// initialize the json file and merge with default data if needed
|
||||
async init() {
|
||||
await this.db.read();
|
||||
if (!this.db.data || !this.db.data.data) {
|
||||
// initialize with default data
|
||||
this.db.data = { data: { ...this.defaultData } };
|
||||
} else {
|
||||
// merge existing data with default data to fill in missing properties
|
||||
this.db.data.data = { ...this.defaultData, ...this.db.data.data };
|
||||
}
|
||||
await this.db.write();
|
||||
}
|
||||
|
||||
async readData(): Promise<T> {
|
||||
await this.db.read();
|
||||
// ensure that the data is merged with default values every time it is read
|
||||
this.db.data!.data = merge({}, this.defaultData, this.db.data!.data);
|
||||
return this.db.data!.data;
|
||||
}
|
||||
|
||||
async writeData(data: T): Promise<void> {
|
||||
this.db.data!.data = data;
|
||||
await this.db.write();
|
||||
}
|
||||
|
||||
// update a specific part of the json file
|
||||
async updateData(callback: UpdateCallback<T>): Promise<void> {
|
||||
await this.db.read();
|
||||
callback(this.db.data!.data);
|
||||
await this.db.write();
|
||||
}
|
||||
}
|
|
@ -18,8 +18,8 @@ export default function MyApp(props: MyAppProps) {
|
|||
return (
|
||||
<CacheProvider value={emotionCache}>
|
||||
<Head>
|
||||
<title>WhensApp</title>
|
||||
<meta name='viewport' content='initial-scale=1, width=device-width' />
|
||||
<title>Stort</title>
|
||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||
</Head>
|
||||
<UserThemeProvider>
|
||||
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
|
||||
|
|
Loading…
Reference in a new issue