From 772a9ef3a271be337fa316a118dbcb32b8eb036a Mon Sep 17 00:00:00 2001 From: nyagami Date: Wed, 12 Jun 2024 16:26:55 +0700 Subject: [PATCH] Feat: Plugin lazy init(#1104) * move plugins to single folder * lazy initial * clean up * fix: restore novels --- App.tsx | 3 +- .../FileManager/FileManager.kt | 14 +--- src/native/FileManager.ts | 4 +- src/plugins/pluginManager.ts | 75 ++++++------------- .../hooks/useGlobalSearch.ts | 3 +- src/screens/reader/ReaderScreen.tsx | 2 +- src/services/backup/legacy/index.ts | 4 +- src/services/backup/utils.ts | 34 ++++----- src/services/epub/import.ts | 4 +- 9 files changed, 50 insertions(+), 93 deletions(-) diff --git a/App.tsx b/App.tsx index 8b066de2..7534b02c 100644 --- a/App.tsx +++ b/App.tsx @@ -14,7 +14,6 @@ import BackgroundService from 'react-native-background-actions'; import { createTables } from '@database/db'; import AppErrorBoundary from '@components/AppErrorBoundary/AppErrorBoundary'; -import { deserializePlugins } from '@plugins/pluginManager'; import Main from './src/navigators/Main'; import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; @@ -32,7 +31,7 @@ Notifications.setNotificationHandler({ }); createTables(); -deserializePlugins().then(() => LottieSplashScreen.hide()); +LottieSplashScreen.hide(); if (!BackgroundService.isRunning()) { MMKVStorage.delete(BACKGROUND_ACTION); } diff --git a/android/app/src/main/java/com/rajarsheechatterjee/FileManager/FileManager.kt b/android/app/src/main/java/com/rajarsheechatterjee/FileManager/FileManager.kt index 1d0af4e6..764b4bf6 100644 --- a/android/app/src/main/java/com/rajarsheechatterjee/FileManager/FileManager.kt +++ b/android/app/src/main/java/com/rajarsheechatterjee/FileManager/FileManager.kt @@ -123,17 +123,9 @@ class FileManager(context: ReactApplicationContext) : } } - @ReactMethod - fun readFile(path: String?, promise: Promise) { - try { - val sb = StringBuilder() - val br = BufferedReader(FileReader(path)) - var line: String? - while (br.readLine().also { line = it } != null) sb.append(line).append('\n') - promise.resolve(sb.toString()) - } catch (e: Exception) { - promise.reject(e) - } + @ReactMethod(isBlockingSynchronousMethod = true) + fun readFile(path: String): String { + return File(path).bufferedReader().readText() } @ReactMethod diff --git a/src/native/FileManager.ts b/src/native/FileManager.ts index 262b1de1..3065698a 100644 --- a/src/native/FileManager.ts +++ b/src/native/FileManager.ts @@ -8,12 +8,12 @@ interface ReadDirResult { interface FileManagerInterface { writeFile: (path: string, content: string) => Promise; - readFile: (path: string) => Promise; + readFile: (path: string) => string; resolveExternalContentUri: (uriString: string) => Promise; copyFile: (sourcePath: string, destPath: string) => Promise; moveFile: (sourcePath: string, destPath: string) => Promise; exists: (filePath: string) => Promise; - mkdir: (filePath: string) => Promise; // create parents; + mkdir: (filePath: string) => Promise; // create parents, and do nothing if exists; unlink: (filePath: string) => Promise; // remove recursively readDir: (dirPath: string) => Promise; // file/sub-folder names pickFolder: () => Promise; // return path of folderc diff --git a/src/plugins/pluginManager.ts b/src/plugins/pluginManager.ts index d01fded8..897910bc 100644 --- a/src/plugins/pluginManager.ts +++ b/src/plugins/pluginManager.ts @@ -19,8 +19,6 @@ import { getRepositoriesFromDb } from '@database/queries/RepositoryQueries'; import { showToast } from '@utils/showToast'; import { PLUGIN_STORAGE } from '@utils/Storages'; -const pluginFilePath = PLUGIN_STORAGE + '/plugins.json'; - const packages: Record = { 'htmlparser2': { Parser }, 'cheerio': { load }, @@ -61,52 +59,6 @@ const initPlugin = (pluginId: string, rawCode: string) => { }; const plugins: Record = {}; -let serializedPlugins: Record; - -const serializePlugin = async ( - pluginId: string, - rawCode: string, - installed: boolean, -) => { - if (!serializedPlugins) { - if (await FileManager.exists(pluginFilePath)) { - const content = await FileManager.readFile(pluginFilePath); - serializedPlugins = JSON.parse(content); - } else { - serializedPlugins = {}; - } - } - if (installed) { - serializedPlugins[pluginId] = rawCode; - } else { - serializedPlugins[pluginId] = undefined; - } - if (!(await FileManager.exists(PLUGIN_STORAGE))) { - await FileManager.mkdir(PLUGIN_STORAGE); - } - await FileManager.writeFile( - pluginFilePath, - JSON.stringify(serializedPlugins), - ); -}; - -const deserializePlugins = () => { - return FileManager.readFile(pluginFilePath) - .then(content => { - serializedPlugins = JSON.parse(content); - Object.entries(serializedPlugins).forEach(([pluginId, script]) => { - if (script) { - const plugin = initPlugin(pluginId, script); - if (plugin) { - plugins[plugin.id] = plugin; - } - } - }); - }) - .catch(() => { - // nothing to read - }); -}; const installPlugin = async ( pluginId: string, @@ -126,7 +78,12 @@ const installPlugin = async ( if (!currentPlugin || newer(plugin.version, currentPlugin.version)) { plugins[plugin.id] = plugin; currentPlugin = plugin; - await serializePlugin(plugin.id, rawCode, true); + + // save plugin code; + const pluginDir = `${PLUGIN_STORAGE}/${pluginId}`; + await FileManager.mkdir(pluginDir); + const filePath = pluginDir + '/index.js'; + await FileManager.writeFile(filePath, rawCode); } return currentPlugin; }); @@ -142,7 +99,10 @@ const uninstallPlugin = async (_plugin: PluginItem) => { store.delete(key); } }); - return serializePlugin(_plugin.id, '', false); + const pluginFilePath = `${PLUGIN_STORAGE}/${_plugin.id}/index.js`; + if (await FileManager.exists(pluginFilePath)) { + await FileManager.unlink(pluginFilePath); + } }; const updatePlugin = async (plugin: PluginItem) => { @@ -168,7 +128,19 @@ const fetchPlugins = async (): Promise => { return uniqBy(reverse(allPlugins), 'id'); }; -const getPlugin = (pluginId: string) => plugins[pluginId]; +const getPlugin = (pluginId: string) => { + if (!plugins[pluginId]) { + const filePath = `${PLUGIN_STORAGE}/${pluginId}/index.js`; + try { + const code = FileManager.readFile(filePath); + const plugin = initPlugin(pluginId, code); + plugins[pluginId] = plugin; + } catch { + // file doesnt exist + } + } + return plugins[pluginId]; +}; const LOCAL_PLUGIN_ID = 'local'; @@ -177,7 +149,6 @@ export { installPlugin, uninstallPlugin, updatePlugin, - deserializePlugins, fetchPlugins, LOCAL_PLUGIN_ID, }; diff --git a/src/screens/GlobalSearchScreen/hooks/useGlobalSearch.ts b/src/screens/GlobalSearchScreen/hooks/useGlobalSearch.ts index 195367ef..c445d66d 100644 --- a/src/screens/GlobalSearchScreen/hooks/useGlobalSearch.ts +++ b/src/screens/GlobalSearchScreen/hooks/useGlobalSearch.ts @@ -70,6 +70,7 @@ export const useGlobalSearch = ({ defaultSearchText }: Props) => { ), ); } catch (error: any) { + const errorMessage = error?.message || String(error); setSearchResults(prevState => prevState.map(prevResult => prevResult.plugin.id === _plugin.id @@ -77,7 +78,7 @@ export const useGlobalSearch = ({ defaultSearchText }: Props) => { ...prevResult, novels: [], isLoading: false, - error: error?.message, + error: errorMessage, } : { ...prevResult }, ), diff --git a/src/screens/reader/ReaderScreen.tsx b/src/screens/reader/ReaderScreen.tsx index 58a82dab..ad77a638 100644 --- a/src/screens/reader/ReaderScreen.tsx +++ b/src/screens/reader/ReaderScreen.tsx @@ -170,7 +170,7 @@ export const ChapterContent = ({ try { const filePath = `${NOVEL_STORAGE}/${novel.pluginId}/${chapter.novelId}/${chapter.id}/index.html`; if (await FileManager.exists(filePath)) { - sourceChapter.chapterText = await FileManager.readFile(filePath); + sourceChapter.chapterText = FileManager.readFile(filePath); } else { await fetchChapter(novel.pluginId, chapter.path) .then(res => { diff --git a/src/services/backup/legacy/index.ts b/src/services/backup/legacy/index.ts index d92b1663..b7cdf719 100644 --- a/src/services/backup/legacy/index.ts +++ b/src/services/backup/legacy/index.ts @@ -51,13 +51,13 @@ export const restoreBackup = async (filePath?: string) => { let novelsString = ''; if (backup.assets && backup.assets[0]) { - novelsString = await FileManager.readFile(backup.assets[0].uri); + novelsString = FileManager.readFile(backup.assets[0].uri); } else if (filePath) { if (!(await FileManager.exists(filePath))) { showToast(getString('backupScreen.legacy.noErrorNovel')); return; //neither backup nor error backup } - novelsString = await FileManager.readFile(filePath); + novelsString = FileManager.readFile(filePath); } const novels: NovelInfo[] = await JSON.parse(novelsString); diff --git a/src/services/backup/utils.ts b/src/services/backup/utils.ts index 03484c52..d2b7a3a2 100644 --- a/src/services/backup/utils.ts +++ b/src/services/backup/utils.ts @@ -119,31 +119,27 @@ export const restoreData = async (cacheDirPath: string) => { await FileManager.readDir(novelDirPath).then(async items => { for (const item of items) { if (!item.isDirectory) { - await FileManager.readFile(item.path).then(content => { - const backupNovel = JSON.parse(content); - if (!backupNovel.cover?.startsWith('http')) { - backupNovel.cover = APP_STORAGE_URI + backupNovel.cover; - } - return _restoreNovelAndChapters(backupNovel); - }); + const backupNovel = JSON.parse(FileManager.readFile(item.path)); + if (!backupNovel.cover?.startsWith('http')) { + backupNovel.cover = APP_STORAGE_URI + backupNovel.cover; + } + await _restoreNovelAndChapters(backupNovel); } } }); // categories - await FileManager.readFile( - cacheDirPath + '/' + BackupEntryName.CATEGORY, - ).then(async content => { - const categories: BackupCategory[] = JSON.parse(content); - for (const category of categories) { - await _restoreCategory(category); - } - }); + const categories: BackupCategory[] = JSON.parse( + FileManager.readFile(cacheDirPath + '/' + BackupEntryName.CATEGORY), + ); + for (const category of categories) { + await _restoreCategory(category); + } // settings - await FileManager.readFile(cacheDirPath + '/' + BackupEntryName.SETTING).then( - content => { - restoreMMKVData(JSON.parse(content)); - }, + restoreMMKVData( + JSON.parse( + FileManager.readFile(cacheDirPath + '/' + BackupEntryName.SETTING), + ), ); }; diff --git a/src/services/epub/import.ts b/src/services/epub/import.ts index b36ae045..b12728ce 100644 --- a/src/services/epub/import.ts +++ b/src/services/epub/import.ts @@ -106,9 +106,7 @@ const insertLocalChapter = ( } catch { // nothing to do } - await FileManager.readFile(path) - .then(r => (chapterText = r)) - .catch(e => reject(e)); + chapterText = FileManager.readFile(path); if (!chapterText) { return; }