diff --git a/src/screens/library/LibraryScreen.tsx b/src/screens/library/LibraryScreen.tsx index 09a788d61..8a1966c25 100644 --- a/src/screens/library/LibraryScreen.tsx +++ b/src/screens/library/LibraryScreen.tsx @@ -210,7 +210,10 @@ const LibraryScreen = ({ navigation }: LibraryScreenProps) => { library[index].id !== 2 && ServiceManager.manager.addTask({ name: 'UPDATE_LIBRARY', - data: library[index].id, + data: { + categoryId: library[index].id, + categoryName: library[index].name, + }, }), }, { @@ -289,6 +292,7 @@ const LibraryScreen = ({ navigation }: LibraryScreenProps) => { ) : null} >; @@ -24,6 +25,7 @@ interface Props { export const LibraryView: React.FC = ({ categoryId, + categoryName, novels, selectedNovelIds, setSelectedNovelIds, @@ -62,7 +64,10 @@ export const LibraryView: React.FC = ({ setRefreshing(true); ServiceManager.manager.addTask({ name: 'UPDATE_LIBRARY', - data: categoryId, + data: { + categoryId, + categoryName, + }, }); setRefreshing(false); }; diff --git a/src/screens/more/TaskQueueScreen.tsx b/src/screens/more/TaskQueueScreen.tsx index 1db4af5b8..c8b58c13f 100644 --- a/src/screens/more/TaskQueueScreen.tsx +++ b/src/screens/more/TaskQueueScreen.tsx @@ -14,14 +14,14 @@ import { showToast } from '../../utils/showToast'; import { getString } from '@strings/translations'; import { Appbar, EmptyView } from '@components'; import { TaskQueueScreenProps } from '@navigators/types'; -import ServiceManager, { BackgroundTask } from '@services/ServiceManager'; +import ServiceManager, { QueuedBackgroundTask } from '@services/ServiceManager'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useMMKVObject } from 'react-native-mmkv'; const DownloadQueue = ({ navigation }: TaskQueueScreenProps) => { const theme = useTheme(); const { bottom } = useSafeAreaInsets(); - const [taskQueue] = useMMKVObject( + const [taskQueue] = useMMKVObject( ServiceManager.manager.STORE_KEY, ); const [isRunning, setIsRunning] = useState(ServiceManager.manager.isRunning); @@ -34,6 +34,8 @@ const DownloadQueue = ({ navigation }: TaskQueueScreenProps) => { } }, [taskQueue]); + //TODO: there should probably be a way to cancel a specific task from this screen + return ( <> { contentContainerStyle={{ flexGrow: 1, paddingBottom: 100 }} keyExtractor={(item, index) => 'task_' + index} data={taskQueue || []} - renderItem={({ item, index }) => ( + renderItem={({ item }) => ( - - {item.name} - {ServiceManager.manager.getTaskDescription(item)} - + {item.meta.name} + {item.meta.progressText ? ( + + {item.meta.progressText} + + ) : null} 0 && index === 0} + indeterminate={ + item.meta.isRunning && item.meta.progress === undefined + } + progress={item.meta.progress} color={theme.primary} - style={{ marginTop: 8 }} + style={{ marginTop: 8, backgroundColor: theme.surface2 }} /> )} diff --git a/src/screens/updates/UpdatesScreen.tsx b/src/screens/updates/UpdatesScreen.tsx index 41b262ee9..f8d2794c0 100644 --- a/src/screens/updates/UpdatesScreen.tsx +++ b/src/screens/updates/UpdatesScreen.tsx @@ -84,7 +84,7 @@ const UpdatesScreen = ({ navigation }: UpdateScreenProps) => { e.preventDefault(); navigation.navigate('MoreStack', { - screen: 'Downloads', + screen: 'TaskQueue', }); } }), diff --git a/src/services/ServiceManager.ts b/src/services/ServiceManager.ts index 7b49c5d6a..1e7b8f9cb 100644 --- a/src/services/ServiceManager.ts +++ b/src/services/ServiceManager.ts @@ -25,7 +25,10 @@ export type BackgroundTask = } | { name: 'UPDATE_LIBRARY'; - data?: number; + data?: { + categoryId?: number; + categoryName?: string; + }; } | { name: 'DRIVE_BACKUP'; data: DriveFile } | { name: 'DRIVE_RESTORE'; data: DriveFile } @@ -37,6 +40,18 @@ export type BackgroundTask = data: { chapterId: number; novelName: string; chapterName: string }; }; +export type BackgroundTaskMetadata = { + name: string; + isRunning: boolean; + progress: number | undefined; + progressText: string | undefined; +}; + +export type QueuedBackgroundTask = { + task: BackgroundTask; + meta: BackgroundTaskMetadata; +}; + export default class ServiceManager { STORE_KEY = 'APP_SERVICE'; private static instance?: ServiceManager; @@ -78,24 +93,48 @@ export default class ServiceManager { }); } } - async executeTask(task: BackgroundTask) { - switch (task.name) { + async executeTask(task: QueuedBackgroundTask) { + let setMeta = ( + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) => { + let taskList = [...this.getTaskList()]; + taskList[0] = { + ...taskList[0], + meta: transformer(taskList[0].meta), + }; + + if (taskList[0].meta.isRunning) { + BackgroundService.updateNotification({ + taskTitle: taskList[0].meta.name, + taskDesc: taskList[0].meta.progressText, + progressBar: { + indeterminate: taskList[0].meta.progress === undefined, + value: (taskList[0].meta.progress || 0) * 100, + max: 100, + }, + }); + } + + setMMKVObject(this.STORE_KEY, taskList); + }; + + switch (task.task.name) { case 'IMPORT_EPUB': - return importEpub(task.data); + return importEpub(task.task.data, setMeta); case 'UPDATE_LIBRARY': - return updateLibrary(task.data); + return updateLibrary(task.task.data || {}, setMeta); case 'DRIVE_BACKUP': - return createDriveBackup(task.data); + return createDriveBackup(task.task.data, setMeta); case 'DRIVE_RESTORE': - return driveRestore(task.data); + return driveRestore(task.task.data, setMeta); case 'SELF_HOST_BACKUP': - return createSelfHostBackup(task.data); + return createSelfHostBackup(task.task.data, setMeta); case 'SELF_HOST_RESTORE': - return selfHostRestore(task.data); + return selfHostRestore(task.task.data, setMeta); case 'MIGRATE_NOVEL': - return migrateNovel(task.data); + return migrateNovel(task.task.data, setMeta); case 'DOWNLOAD_CHAPTER': - return downloadChapter(task.data); + return downloadChapter(task.task.data, setMeta); default: return; } @@ -121,11 +160,11 @@ export default class ServiceManager { } try { await manager.executeTask(currentTask); - doneTasks[currentTask.name] += 1; + doneTasks[currentTask.task.name] += 1; } catch (error: any) { await Notifications.scheduleNotificationAsync({ content: { - title: currentTask.name, + title: currentTask.meta.name, body: error?.message || String(error), }, trigger: null, @@ -148,46 +187,71 @@ export default class ServiceManager { }); } } - getTaskDescription(task: BackgroundTask) { + getTaskName(task: BackgroundTask) { switch (task.name) { case 'DOWNLOAD_CHAPTER': - return task.data.chapterName; + return ( + 'Download ' + task.data.novelName + ' - ' + task.data.chapterName + ); case 'IMPORT_EPUB': - return task.data.filename; + return 'Import Epub ' + task.data.filename; case 'MIGRATE_NOVEL': - return task.data.fromNovel.name; + return 'Migrate Novel ' + task.data.fromNovel.name; + case 'UPDATE_LIBRARY': + if (task.data !== undefined) { + return 'Update Category ' + task.data.categoryName; + } + return 'Update Library'; + case 'DRIVE_BACKUP': + return 'Drive Backup'; + case 'DRIVE_RESTORE': + return 'Drive Restore'; + case 'SELF_HOST_BACKUP': + return 'Self Host Backup'; + case 'SELF_HOST_RESTORE': + return 'Self Host Restore'; default: - return task.data?.toString() || 'No data'; + return 'Unknown Task'; } } getTaskList() { - return getMMKVObject>(this.STORE_KEY) || []; + return getMMKVObject>(this.STORE_KEY) || []; } addTask(tasks: BackgroundTask | BackgroundTask[]) { const currentTasks = this.getTaskList(); const addableTasks = (Array.isArray(tasks) ? tasks : [tasks]).filter( task => this.isMultiplicableTask(task) || - !currentTasks.some(_t => _t.name === task.name), + !currentTasks.some(_t => _t.task.name === task.name), ); if (addableTasks.length) { - setMMKVObject(this.STORE_KEY, currentTasks.concat(addableTasks)); + let newTasks: QueuedBackgroundTask[] = addableTasks.map(task => ({ + task, + meta: { + name: this.getTaskName(task), + isRunning: false, + progress: undefined, + progressText: undefined, + }, + })); + + setMMKVObject(this.STORE_KEY, currentTasks.concat(newTasks)); this.start(); } } removeTasksByName(name: BackgroundTask['name']) { const taskList = this.getTaskList(); - if (taskList[0]?.name === name) { + if (taskList[0]?.task?.name === name) { this.pause(); setMMKVObject( this.STORE_KEY, - taskList.filter(t => t.name !== name), + taskList.filter(t => t.task.name !== name), ); this.resume(); } else { setMMKVObject( this.STORE_KEY, - taskList.filter(t => t.name !== name), + taskList.filter(t => t.task.name !== name), ); } } diff --git a/src/services/backup/drive/index.ts b/src/services/backup/drive/index.ts index 9585469c8..32d03db7d 100644 --- a/src/services/backup/drive/index.ts +++ b/src/services/backup/drive/index.ts @@ -1,71 +1,85 @@ import { DriveFile } from '@api/drive/types'; import { sleep } from '@utils/sleep'; -import BackgroundService from 'react-native-background-actions'; import { exists } from '@api/drive'; import { getString } from '@strings/translations'; import { CACHE_DIR_PATH, prepareBackupData, restoreData } from '../utils'; import { download, updateMetadata, uploadMedia } from '@api/drive/request'; import { ZipBackupName } from '../types'; import { ROOT_STORAGE } from '@utils/Storages'; +import { BackgroundTaskMetadata } from '@services/ServiceManager'; -export const createDriveBackup = (backupFolder: DriveFile) => { - return BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.preparingData'), - progressBar: { - indeterminate: true, - value: 0, - max: 3, +export const createDriveBackup = async ( + backupFolder: DriveFile, + setMeta: ( + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) => void, +) => { + setMeta(meta => ({ + ...meta, + isRunning: true, + progress: 0 / 3, + progressText: getString('backupScreen.preparingData'), + })); + + await prepareBackupData(CACHE_DIR_PATH); + + setMeta(meta => ({ + ...meta, + progress: 1 / 3, + progressText: getString('backupScreen.uploadingData'), + })); + + await sleep(500); + + let file = await uploadMedia(CACHE_DIR_PATH); + + await updateMetadata( + file.id, + { + name: ZipBackupName.DATA, + mimeType: 'application/zip', + parents: [backupFolder.id], }, - }) - .then(() => prepareBackupData(CACHE_DIR_PATH)) - .then(() => - BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.uploadingData'), - progressBar: { - indeterminate: true, - value: 1, - max: 3, - }, - }), - ) - .then(() => sleep(500)) - .then(() => uploadMedia(CACHE_DIR_PATH)) - .then(file => - updateMetadata( - file.id, - { - name: ZipBackupName.DATA, - mimeType: 'application/zip', - parents: [backupFolder.id], - }, - file.parents[0], - ), - ) - .then(() => - BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.uploadingDownloadedFiles'), - progressBar: { - indeterminate: true, - value: 2, - max: 3, - }, - }), - ) - .then(() => uploadMedia(ROOT_STORAGE)) - .then(file => - updateMetadata( - file.id, - { - name: ZipBackupName.DOWNLOAD, - mimeType: 'application/zip', - parents: [backupFolder.id], - }, - file.parents[0], - ), - ); + file.parents[0], + ); + + setMeta(meta => ({ + ...meta, + progress: 2 / 3, + progressText: getString('backupScreen.uploadingDownloadedFiles'), + })); + + let file2 = await uploadMedia(ROOT_STORAGE); + await updateMetadata( + file2.id, + { + name: ZipBackupName.DOWNLOAD, + mimeType: 'application/zip', + parents: [backupFolder.id], + }, + file2.parents[0], + ); + + setMeta(meta => ({ + ...meta, + progress: 3 / 3, + isRunning: false, + })); }; -export const driveRestore = async (backupFolder: DriveFile) => { +export const driveRestore = async ( + backupFolder: DriveFile, + setMeta: ( + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) => void, +) => { + setMeta(meta => ({ + ...meta, + isRunning: true, + progress: 0 / 3, + progressText: getString('backupScreen.downloadingData'), + })); + const zipDataFile = await exists(ZipBackupName.DATA, false, backupFolder.id); const zipDownloadFile = await exists( ZipBackupName.DOWNLOAD, @@ -75,37 +89,30 @@ export const driveRestore = async (backupFolder: DriveFile) => { if (!zipDataFile || !zipDownloadFile) { throw new Error(getString('backupScreen.invalidBackupFolder')); } - await BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.downloadingData'), - progressBar: { - indeterminate: true, - value: 0, - max: 3, - }, - }) - .then(() => download(zipDataFile, CACHE_DIR_PATH)) - .then(() => sleep(500)) - .then(() => - BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.restoringData'), - progressBar: { - indeterminate: true, - value: 1, - max: 3, - }, - }), - ) - .then(() => restoreData(CACHE_DIR_PATH)) - .then(() => sleep(500)) - .then(() => - BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.downloadingDownloadedFiles'), - progressBar: { - indeterminate: true, - value: 2, - max: 3, - }, - }), - ) - .then(() => download(zipDownloadFile, ROOT_STORAGE)); + + await download(zipDataFile, CACHE_DIR_PATH); + await sleep(500); + + setMeta(meta => ({ + ...meta, + progress: 1 / 3, + progressText: getString('backupScreen.restoringData'), + })); + + await restoreData(CACHE_DIR_PATH); + await sleep(500); + + setMeta(meta => ({ + ...meta, + progress: 2 / 3, + progressText: getString('backupScreen.downloadingDownloadedFiles'), + })); + + await download(zipDownloadFile, ROOT_STORAGE); + + setMeta(meta => ({ + ...meta, + progress: 3 / 3, + isRunning: false, + })); }; diff --git a/src/services/backup/selfhost/index.ts b/src/services/backup/selfhost/index.ts index 2e0438d09..4c3a524f6 100644 --- a/src/services/backup/selfhost/index.ts +++ b/src/services/backup/selfhost/index.ts @@ -1,90 +1,96 @@ import { sleep } from '@utils/sleep'; -import BackgroundService from 'react-native-background-actions'; import { download, upload } from '@api/remote'; import { getString } from '@strings/translations'; import { CACHE_DIR_PATH, prepareBackupData, restoreData } from '../utils'; import { ZipBackupName } from '../types'; import { ROOT_STORAGE } from '@utils/Storages'; +import { BackgroundTaskMetadata } from '@services/ServiceManager'; export interface SelfHostData { host: string; backupFolder: string; } -export const createSelfHostBackup = ({ host, backupFolder }: SelfHostData) => { - return BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.preparingData'), - progressBar: { - indeterminate: true, - value: 0, - max: 3, - }, - }) - .then(() => prepareBackupData(CACHE_DIR_PATH)) - .then(() => - BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.uploadingData'), - progressBar: { - indeterminate: true, - value: 1, - max: 3, - }, - }), - ) - .then(() => sleep(200)) - .then(() => upload(host, backupFolder, ZipBackupName.DATA, CACHE_DIR_PATH)) - .then(() => - BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.uploadingDownloadedFiles'), - progressBar: { - indeterminate: true, - value: 2, - max: 3, - }, - }), - ) - .then(() => sleep(200)) - .then(() => - upload(host, backupFolder, ZipBackupName.DOWNLOAD, ROOT_STORAGE), - ); +export const createSelfHostBackup = async ( + { host, backupFolder }: SelfHostData, + setMeta: ( + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) => void, +) => { + setMeta(meta => ({ + ...meta, + isRunning: true, + progress: 0 / 3, + progressText: getString('backupScreen.preparingData'), + })); + + await prepareBackupData(CACHE_DIR_PATH); + + setMeta(meta => ({ + ...meta, + progress: 1 / 3, + progressText: getString('backupScreen.uploadingData'), + })); + + await sleep(200); + + await upload(host, backupFolder, ZipBackupName.DATA, CACHE_DIR_PATH); + + setMeta(meta => ({ + ...meta, + progress: 2 / 3, + progressText: getString('backupScreen.uploadingDownloadedFiles'), + })); + + await sleep(200); + + await upload(host, backupFolder, ZipBackupName.DOWNLOAD, ROOT_STORAGE); + + setMeta(meta => ({ + ...meta, + progress: 3 / 3, + isRunning: false, + })); }; -export const selfHostRestore = ({ host, backupFolder }: SelfHostData) => { - return BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.downloadingData'), - progressBar: { - indeterminate: true, - value: 0, - max: 3, - }, - }) - .then(() => - download(host, backupFolder, ZipBackupName.DATA, CACHE_DIR_PATH), - ) - .then(() => - BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.restoringData'), - progressBar: { - indeterminate: true, - value: 1, - max: 3, - }, - }), - ) - .then(() => sleep(200)) - .then(() => restoreData(CACHE_DIR_PATH)) - .then(() => - BackgroundService.updateNotification({ - taskDesc: getString('backupScreen.downloadingDownloadedFiles'), - progressBar: { - indeterminate: true, - value: 2, - max: 3, - }, - }), - ) - .then(() => sleep(200)) - .then(() => - download(host, backupFolder, ZipBackupName.DOWNLOAD, ROOT_STORAGE), - ); +export const selfHostRestore = async ( + { host, backupFolder }: SelfHostData, + setMeta: ( + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) => void, +) => { + setMeta(meta => ({ + ...meta, + isRunning: true, + progress: 0 / 3, + progressText: getString('backupScreen.downloadingData'), + })); + + await download(host, backupFolder, ZipBackupName.DATA, CACHE_DIR_PATH); + + setMeta(meta => ({ + ...meta, + progress: 1 / 3, + progressText: getString('backupScreen.restoringData'), + })); + + await sleep(200); + + await restoreData(CACHE_DIR_PATH); + + setMeta(meta => ({ + ...meta, + progress: 2 / 3, + progressText: getString('backupScreen.downloadingDownloadedFiles'), + })); + + await sleep(200); + + await download(host, backupFolder, ZipBackupName.DOWNLOAD, ROOT_STORAGE); + + setMeta(meta => ({ + ...meta, + progress: 3 / 3, + isRunning: false, + })); }; diff --git a/src/services/download/downloadChapter.ts b/src/services/download/downloadChapter.ts index 9a6157957..e19c95ad3 100644 --- a/src/services/download/downloadChapter.ts +++ b/src/services/download/downloadChapter.ts @@ -1,5 +1,4 @@ import * as cheerio from 'cheerio'; -import BackgroundService from 'react-native-background-actions'; import FileManager from '@native/FileManager'; import { NOVEL_STORAGE } from '@utils/Storages'; import { Plugin } from '@plugins/types'; @@ -10,6 +9,7 @@ import { getChapter } from '@database/queries/ChapterQueries'; import { sleep } from '@utils/sleep'; import { getNovelById } from '@database/queries/NovelQueries'; import { db } from '@database/db'; +import { BackgroundTaskMetadata } from '@services/ServiceManager'; const createChapterFolder = async ( path: string, @@ -57,7 +57,17 @@ const downloadFiles = async ( await FileManager.writeFile(folder + '/index.html', loadedCheerio.html()); }; -export const downloadChapter = async ({ chapterId }: { chapterId: number }) => { +export const downloadChapter = async ( + { chapterId }: { chapterId: number }, + setMeta: ( + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) => void, +) => { + setMeta(meta => ({ + ...meta, + isRunning: true, + })); + const chapter = await getChapter(chapterId); if (!chapter) { throw new Error('Chapter not found with id: ' + chapterId); @@ -73,14 +83,6 @@ export const downloadChapter = async ({ chapterId }: { chapterId: number }) => { if (!plugin) { throw new Error(getString('downloadScreen.pluginNotFound')); } - await BackgroundService.updateNotification({ - taskTitle: getString('downloadScreen.downloadingNovel', { - name: novel.name, - }), - taskDesc: getString('downloadScreen.chapterName', { - name: chapter.name, - }), - }); const chapterText = await plugin.parseChapter(chapter.path); if (chapterText && chapterText.length) { await downloadFiles(chapterText, plugin, novel.id, chapter.id); @@ -93,4 +95,10 @@ export const downloadChapter = async ({ chapterId }: { chapterId: number }) => { } else { throw new Error(getString('downloadScreen.chapterEmptyOrScrapeError')); } + + setMeta(meta => ({ + ...meta, + progress: 1, + isRunning: false, + })); }; diff --git a/src/services/epub/import.ts b/src/services/epub/import.ts index 8529e1827..cac383bb0 100644 --- a/src/services/epub/import.ts +++ b/src/services/epub/import.ts @@ -1,4 +1,3 @@ -import BackgroundService from 'react-native-background-actions'; import ZipArchive from '@native/ZipArchive'; import dayjs from 'dayjs'; import { @@ -11,6 +10,7 @@ import FileManager from '@native/FileManager'; import EpubUtil from '@native/EpubUtil'; import { NOVEL_STORAGE } from '@utils/Storages'; import { db } from '@database/db'; +import { BackgroundTaskMetadata } from '@services/ServiceManager'; const insertLocalNovel = ( name: string, @@ -134,13 +134,24 @@ const insertLocalChapter = ( }); }; -export const importEpub = async ({ - uri, - filename, -}: { - uri: string; - filename: string; -}) => { +export const importEpub = async ( + { + uri, + filename, + }: { + uri: string; + filename: string; + }, + setMeta: ( + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) => void, +) => { + setMeta(meta => ({ + ...meta, + isRunning: true, + progress: 0, + })); + const epubFilePath = FileManager.ExternalCachesDirectoryPath + '/novel.epub'; await FileManager.copyFile(uri, epubFilePath); const epubDirPath = FileManager.ExternalCachesDirectoryPath + '/epub'; @@ -164,26 +175,17 @@ export const importEpub = async ({ const now = dayjs().toISOString(); const filePathSet = new Set(); if (novel.chapters) { - BackgroundService.updateNotification({ - taskTitle: getString('advancedSettingsScreen.importNovel'), - taskDesc: '0/' + novel.chapters.length, - progressBar: { - value: 0, - max: novel.chapters.length, - }, - }); for (let i = 0; i < novel.chapters?.length; i++) { - BackgroundService.updateNotification({ - taskDesc: i + 1 + '/' + novel.chapters.length, - progressBar: { - value: i + 1, - max: novel.chapters.length, - }, - }); const chapter = novel.chapters[i]; if (!chapter.name) { chapter.name = chapter.path.split(/[\\\/]/).pop() || 'unknown'; } + + setMeta(meta => ({ + ...meta, + progressText: chapter.name, + })); + const filePaths = await insertLocalChapter( novelId, i, @@ -192,32 +194,32 @@ export const importEpub = async ({ now, ); filePaths.forEach(filePath => filePathSet.add(filePath)); + + setMeta(meta => ({ + ...meta, + progress: i / novel.chapters.length, + })); } } const novelDir = NOVEL_STORAGE + '/local/' + novelId; - BackgroundService.updateNotification({ - taskTitle: getString('advancedSettingsScreen.importStaticFiles'), - taskDesc: '0/' + filePathSet.size, - progressBar: { - value: 0, - max: filePathSet.size, - }, - }); - let cnt = 1; + + setMeta(meta => ({ + ...meta, + progressText: getString('advancedSettingsScreen.importStaticFiles'), + })); + for (let filePath of filePathSet) { - BackgroundService.updateNotification({ - taskDesc: cnt + '/' + filePathSet.size, - progressBar: { - value: cnt, - max: filePathSet.size, - }, - }); if (await FileManager.exists(filePath)) { await FileManager.moveFile( filePath, novelDir + '/' + filePath.split(/[\\\/]/).pop(), ); } - cnt += 1; } + + setMeta(meta => ({ + ...meta, + progress: 1, + isRunning: false, + })); }; diff --git a/src/services/migrate/migrateNovel.ts b/src/services/migrate/migrateNovel.ts index ff5b0e09e..825c9249d 100644 --- a/src/services/migrate/migrateNovel.ts +++ b/src/services/migrate/migrateNovel.ts @@ -1,5 +1,3 @@ -import BackgroundService from 'react-native-background-actions'; - import { NovelInfo, ChapterInfo } from '@database/types'; import { getNovelByPath, @@ -18,7 +16,9 @@ import { NOVEL_SETTINSG_PREFIX, } from '@hooks/persisted/useNovel'; import { sleep } from '@utils/sleep'; -import ServiceManager from '@services/ServiceManager'; +import ServiceManager, { + BackgroundTaskMetadata, +} from '@services/ServiceManager'; import { db } from '@database/db'; export interface MigrateNovelData { @@ -49,11 +49,17 @@ const sortChaptersByNumber = (novelName: string, chapters: ChapterInfo[]) => { }); }; -export const migrateNovel = async ({ - pluginId, - fromNovel, - toNovelPath, -}: MigrateNovelData) => { +export const migrateNovel = async ( + { pluginId, fromNovel, toNovelPath }: MigrateNovelData, + setMeta: ( + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) => void, +) => { + setMeta(meta => ({ + ...meta, + isRunning: true, + })); + let fromChapters = await getNovelChapters(fromNovel.id); let toNovel = await getNovelByPath(toNovelPath, pluginId); let toChapters: ChapterInfo[]; @@ -165,12 +171,12 @@ export const migrateNovel = async ({ setLastRead(toChapter); } - await BackgroundService.updateNotification({ - taskDesc: '(' + (fromPointer + 1) + '/' + fromChapters.length + ')', - progressBar: { max: fromChapters.length, value: fromPointer + 1 }, - }); - ++fromPointer; ++toPointer; } + + setMeta(meta => ({ + ...meta, + isRunning: false, + })); }; diff --git a/src/services/updates/index.ts b/src/services/updates/index.ts index c5281e872..c82cdd7cb 100644 --- a/src/services/updates/index.ts +++ b/src/services/updates/index.ts @@ -1,5 +1,3 @@ -import BackgroundService from 'react-native-background-actions'; - import { getLibraryWithCategory, getLibraryNovelsFromDb, @@ -13,8 +11,24 @@ import { MMKVStorage, getMMKVObject } from '@utils/mmkv/mmkv'; import { LAST_UPDATE_TIME } from '@hooks/persisted/useUpdates'; import dayjs from 'dayjs'; import { APP_SETTINGS, AppSettings } from '@hooks/persisted/useSettings'; +import { BackgroundTaskMetadata } from '@services/ServiceManager'; + +const updateLibrary = async ( + { + categoryId, + }: { + categoryId?: number; + }, + setMeta: ( + transformer: (meta: BackgroundTaskMetadata) => BackgroundTaskMetadata, + ) => void, +) => { + setMeta(meta => ({ + ...meta, + isRunning: true, + progress: 0, + })); -const updateLibrary = async (categoryId?: number) => { const { downloadNewChapters, refreshNovelMetadata, onlyUpdateOngoingNovels } = getMMKVObject(APP_SETTINGS) || {}; const options: UpdateNovelOptions = { @@ -38,6 +52,12 @@ const updateLibrary = async (categoryId?: number) => { if (libraryNovels.length > 0) { MMKVStorage.set(LAST_UPDATE_TIME, dayjs().format('YYYY-MM-DD HH:mm:ss')); for (let i = 0; i < libraryNovels.length; i++) { + setMeta(meta => ({ + ...meta, + progressText: libraryNovels[i].name, + progress: i / libraryNovels.length, + })); + try { await updateNovel( libraryNovels[i].pluginId, @@ -45,14 +65,6 @@ const updateLibrary = async (categoryId?: number) => { libraryNovels[i].id, options, ); - /** - * Update notification - */ - await BackgroundService.updateNotification({ - taskTitle: '(' + (i + 1) + '/' + libraryNovels.length + ')', - taskDesc: libraryNovels[i].name, - progressBar: { max: libraryNovels.length, value: i + 1 }, - }); await sleep(1000); } catch (error: any) { showToast(libraryNovels[i].name + ': ' + error.message); @@ -62,6 +74,12 @@ const updateLibrary = async (categoryId?: number) => { } else { showToast("There's no novel to be updated"); } + + setMeta(meta => ({ + ...meta, + progress: 1, + isRunning: false, + })); }; export { updateLibrary };