GoonCord/src/lib/api/storage/backends.ts

95 lines
3.4 KiB
TypeScript
Raw Normal View History

import { ReactNative as RN } from "@metro/common";
import { FileManager, MMKVManager } from "../native/modules";
2024-02-23 15:11:35 +08:00
export interface StorageBackend {
get: () => unknown | Promise<unknown>;
set: (data: unknown) => void | Promise<void>;
}
const ILLEGAL_CHARS_REGEX = /[<>:"\/\\|?*]/g;
const filePathFixer = (file: string): string => RN.Platform.select({
default: file,
ios: FileManager.saveFileToGallery ? file : `Documents/${file}`,
});
const getMMKVPath = (name: string): string => {
if (ILLEGAL_CHARS_REGEX.test(name)) {
// Replace forbidden characters with hyphens
name = name.replace(ILLEGAL_CHARS_REGEX, '-').replace(/-+/g, '-');
}
return `vd_mmkv/${name}`;
}
export const purgeStorage = async (store: string) => {
if (await MMKVManager.getItem(store)) {
MMKVManager.removeItem(store);
}
const mmkvPath = getMMKVPath(store);
if (await FileManager.fileExists(`${FileManager.getConstants().DocumentsDirPath}/${mmkvPath}`)) {
await FileManager.removeFile?.("documents", mmkvPath);
}
}
2024-02-24 00:29:27 +08:00
export const createMMKVBackend = (store: string, defaultData = {}) => {
const mmkvPath = getMMKVPath(store);
2024-02-24 00:29:27 +08:00
const defaultStr = JSON.stringify(defaultData);
return createFileBackend(mmkvPath, defaultData, (async () => {
2024-02-08 14:17:58 +08:00
const path = `${FileManager.getConstants().DocumentsDirPath}/${mmkvPath}`;
if (await FileManager.fileExists(path)) return;
2024-02-24 00:29:27 +08:00
let oldData = await MMKVManager.getItem(store) ?? defaultStr;
2024-02-08 14:17:58 +08:00
// From the testing on Android, it seems to return this if the data is too large
if (oldData === "!!LARGE_VALUE!!") {
const cachePath = `${FileManager.getConstants().CacheDirPath}/mmkv/${store}`;
if (await FileManager.fileExists(cachePath)) {
oldData = await FileManager.readFile(cachePath, "utf8")
} else {
console.log(`${store}: Experienced data loss :(`);
2024-02-24 00:29:27 +08:00
oldData = defaultStr;
}
2024-02-08 14:17:58 +08:00
}
2024-02-08 14:17:58 +08:00
try {
JSON.parse(oldData);
} catch {
console.error(`${store} had an unparseable data while migrating`);
2024-02-24 00:29:27 +08:00
oldData = defaultStr;
2024-02-08 14:17:58 +08:00
}
await FileManager.writeFile("documents", filePathFixer(mmkvPath), oldData, "utf8");
if (await MMKVManager.getItem(store) !== null) {
MMKVManager.removeItem(store);
console.log(`Successfully migrated ${store} store from MMKV storage to fs`);
}
})());
}
2024-02-24 00:29:27 +08:00
export const createFileBackend = (file: string, defaultData = {}, migratePromise?: Promise<void>): StorageBackend => {
return {
get: async () => {
await migratePromise;
2023-04-05 20:40:17 +01:00
const path = `${FileManager.getConstants().DocumentsDirPath}/${file}`;
2024-02-08 14:17:58 +08:00
if (await FileManager.fileExists(path)) {
const content = await FileManager.readFile(path, "utf8");
try {
return JSON.parse(content);
} catch {
// Corrupted content, ignore
}
}
2024-02-24 00:29:27 +08:00
await FileManager.writeFile("documents", filePathFixer(file), JSON.stringify(defaultData), "utf8");
2023-04-05 20:40:17 +01:00
return JSON.parse(await FileManager.readFile(path, "utf8"));
},
set: async (data) => {
await migratePromise;
await FileManager.writeFile("documents", filePathFixer(file), JSON.stringify(data), "utf8");
}
};
2023-04-05 22:18:22 +02:00
};