Skip to content
This repository has been archived by the owner on Feb 6, 2024. It is now read-only.

Commit

Permalink
[Lib > Storage] Move from MMKVManager to FS (#206)
Browse files Browse the repository at this point in the history
* init migration

* fixes
  • Loading branch information
pylixonly authored Dec 17, 2023
1 parent c585300 commit 80efe70
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 14 deletions.
2 changes: 2 additions & 0 deletions src/def.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,13 @@ interface FileManager {
* @returns Promise that resolves to path of the file once it got written
*/
writeFile(storageDir: "cache" | "documents", path: string, data: string, encoding: "base64" | "utf8"): Promise<string>;
removeFile(storageDir: "cache" | "documents", path: string): Promise<unknown>;
getConstants: () => {
/**
* The path the `documents` storage dir (see {@link writeFile}) represents.
*/
DocumentsDirPath: string;
CacheDirPath: string;
};
/**
* Will apparently cease to exist some time in the future so please use {@link getConstants} instead.
Expand Down
2 changes: 1 addition & 1 deletion src/lib/metro/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const constants = findByProps("Fonts", "Permissions");
export const channels = findByProps("getVoiceChannelId");
export const i18n = findByProps("Messages");
export const url = findByProps("openURL", "openDeeplink");
export const toasts = find(m => m.open && m.close && !m.startDrag && !m.init && !m.openReplay && !m.setAlwaysOnTop);
export const toasts = find(m => m.open && m.close && !m.startDrag && !m.init && !m.openReplay && !m.setAlwaysOnTop && !m.setAccountFlag);

// Compatible with pre-204201 versions since createThemedStyleSheet is undefined.
export const stylesheet = {
Expand Down
6 changes: 3 additions & 3 deletions src/lib/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PluginManifest, Plugin } from "@types";
import { safeFetch } from "@lib/utils";
import { awaitSyncWrapper, createMMKVBackend, createStorage, wrapSync } from "@lib/storage";
import { awaitSyncWrapper, createMMKVBackend, createStorage, purgeStorage, wrapSync } from "@lib/storage";
import { MMKVManager } from "@lib/native";
import { allSettled } from "@lib/polyfills";
import logger, { logModule } from "@lib/logger";
Expand Down Expand Up @@ -117,12 +117,12 @@ export function stopPlugin(id: string, disable = true) {
disable && (plugin.enabled = false);
}

export function removePlugin(id: string) {
export async function removePlugin(id: string) {
if (!id.endsWith("/")) id += "/";
const plugin = plugins[id];
if (plugin.enabled) stopPlugin(id);
MMKVManager.removeItem(id);
delete plugins[id];
await purgeStorage(id);
}

export async function initPlugins() {
Expand Down
72 changes: 62 additions & 10 deletions src/lib/storage/backends.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,77 @@
import { StorageBackend } from "@types";
import { ReactNative as RN } from "@metro/common";
import { MMKVManager, FileManager } from "@lib/native";
import { ReactNative as RN } from "@metro/common";

const ILLEGAL_CHARS_REGEX = /[<>:"\/\\|?*]/g;

export const createMMKVBackend = (store: string): StorageBackend => ({
get: async () => JSON.parse((await MMKVManager.getItem(store)) ?? "{}"),
set: (data) => MMKVManager.setItem(store, JSON.stringify(data)),
const filePathFixer = (file: string): string => RN.Platform.select({
default: file,
ios: FileManager.saveFileToGallery ? file : `Documents/${file}`,
});

export const createFileBackend = (file: string): StorageBackend => {
const filePathFixer: (file: string) => string = RN.Platform.select({
default: (f) => f,
ios: (f) => FileManager.saveFileToGallery ? f : `Documents/${f}`,
});
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);
}
}

export const createMMKVBackend = (store: string) => {
const mmkvPath = getMMKVPath(store);
return createFileBackend(mmkvPath, (async () => {
try {
const path = `${FileManager.getConstants().DocumentsDirPath}/${mmkvPath}`;
if (await FileManager.fileExists(path)) return;

let oldData = await MMKVManager.getItem(store) ?? "{}";

// 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 :(`);
oldData = "{}";
}
}

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`);
}
} catch (err) {
console.error("Failed to migrate to fs from MMKVManager ", err)
}
})());
}

export const createFileBackend = (file: string, migratePromise?: Promise<void>): StorageBackend => {
let created: boolean;
return {
get: async () => {
await migratePromise;
const path = `${FileManager.getConstants().DocumentsDirPath}/${file}`;
if (!created && !(await FileManager.fileExists(path))) return (created = true), FileManager.writeFile("documents", filePathFixer(file), "{}", "utf8");
return JSON.parse(await FileManager.readFile(path, "utf8"));
},
set: async (data) => void await FileManager.writeFile("documents", filePathFixer(file), JSON.stringify(data), "utf8"),
set: async (data) => {
await migratePromise;
await FileManager.writeFile("documents", filePathFixer(file), JSON.stringify(data), "utf8");
}
};
};

0 comments on commit 80efe70

Please sign in to comment.