diff --git a/apps/mobile/app/screens/settings/settings-data.tsx b/apps/mobile/app/screens/settings/settings-data.tsx index f1c584dfe..46950b80d 100644 --- a/apps/mobile/app/screens/settings/settings-data.tsx +++ b/apps/mobile/app/screens/settings/settings-data.tsx @@ -400,6 +400,14 @@ export const settingsGroups: SettingSection[] = [ type: "switch", property: "disableSync" }, + { + id: "background-sync", + name: "Background sync (Experimental)", + description: + "Periodically wake up the app in background to sync your notes from other devices.", + type: "switch", + property: "backgroundSync" + }, { id: "sync-issues-fix", name: "Having problems with sync", diff --git a/apps/mobile/app/services/background-sync.ts b/apps/mobile/app/services/background-sync.ts index 07d06df28..c8fae2308 100644 --- a/apps/mobile/app/services/background-sync.ts +++ b/apps/mobile/app/services/background-sync.ts @@ -21,6 +21,11 @@ import { beginBackgroundTask, endBackgroundTask } from "react-native-begin-background-task"; +import BackgroundFetch from "@ammarahmed/react-native-background-fetch"; +import { DatabaseLogger, db } from "../common/database"; +import { AppState, AppRegistry } from "react-native"; +import Notifications from "./notifications"; +import SettingsService from "./settings"; async function doInBackground(callback: () => Promise) { if (Platform.OS === "ios") { @@ -45,107 +50,116 @@ async function doInBackground(callback: () => Promise) { } } -// import BackgroundFetch from "react-native-background-fetch"; -// import { DatabaseLogger, db } from "../common/database"; -// import { AppState, AppRegistry } from "react-native"; -// import Notifications from "./notifications"; -// import SettingsService from "./settings"; +let backgroundFetchStarted = false; +async function start() { + if (backgroundFetchStarted) return; + backgroundFetchStarted = true; + if (!SettingsService.getProperty("backgroundSync")) { + return; + } + // BackgroundFetch event handler. + const onEvent = async (taskId: string) => { + DatabaseLogger.info( + `BACKGROUND FETCH ON EVENT ${taskId}, ${AppState.currentState}}` + ); + // Do your background work... + await onBackgroundSyncStarted(); + // IMPORTANT: You must signal to the OS that your task is complete. + BackgroundFetch.finish(taskId); + }; -// let backgroundFetchStarted = false; -// async function start() { -// if (backgroundFetchStarted) return; -// backgroundFetchStarted = true; -// // BackgroundFetch event handler. -// const onEvent = async (taskId: string) => { -// console.log("[BackgroundFetch] task: ", taskId, AppState.currentState); -// // Do your background work... -// await onBackgroundSyncStarted(); -// // IMPORTANT: You must signal to the OS that your task is complete. -// BackgroundFetch.finish(taskId); -// }; + // Timeout callback is executed when your Task has exceeded its allowed running-time. + // You must stop what you're doing immediately BackgroundFetch.finish(taskId) + const onTimeout = async (taskId: string) => { + DatabaseLogger.info(`BACKGROUND FETCH TIMEOUT: ${taskId}`); + BackgroundFetch.finish(taskId); + }; -// // Timeout callback is executed when your Task has exceeded its allowed running-time. -// // You must stop what you're doing immediately BackgroundFetch.finish(taskId) -// const onTimeout = async (taskId: string) => { -// console.warn("[BackgroundFetch] TIMEOUT: ", taskId); -// BackgroundFetch.finish(taskId); -// }; + // Initialize BackgroundFetch only once when component mounts. + const status = await BackgroundFetch.configure( + { + minimumFetchInterval: 15, + enableHeadless: true, + startOnBoot: true, + stopOnTerminate: false, + requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY + }, + onEvent, + onTimeout + ); + DatabaseLogger.info(`[BackgroundFetch] configure status: ${status}`); +} -// // Initialize BackgroundFetch only once when component mounts. -// const status = await BackgroundFetch.configure( -// { -// minimumFetchInterval: 15, -// enableHeadless: true, -// startOnBoot: true, -// stopOnTerminate: false, -// requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY -// }, -// onEvent, -// onTimeout -// ); -// DatabaseLogger.info(`[BackgroundFetch] configure status: ${status}`); -// console.log(`[BackgroundFetch] configure status: ${status}`); -// } +const task = async (event: { taskId: string; timeout: boolean }) => { + // Get task id from event {}: + const taskId = event.taskId; + const isTimeout = event.timeout; // <-- true when your background-time has expired. + if (isTimeout) { + console.log(`BACKGROUND FETCH TIMEOUT: ${taskId}`); + BackgroundFetch.finish(taskId); + return; + } + DatabaseLogger.info("BACKGROUND SYNC START" + taskId + AppState.currentState); + await onBackgroundSyncStarted(); + BackgroundFetch.finish(taskId); +}; -// const task = async (event: { taskId: string; timeout: boolean }) => { -// // Get task id from event {}: -// const taskId = event.taskId; -// const isTimeout = event.timeout; // <-- true when your background-time has expired. -// if (isTimeout) { -// console.log("[BackgroundFetch] Headless TIMEOUT:", taskId); -// BackgroundFetch.finish(taskId); -// return; -// } -// DatabaseLogger.info( -// "[BackgroundFetch HeadlessTask] start: " + taskId + AppState.currentState -// ); -// await onBackgroundSyncStarted(); -// BackgroundFetch.finish(taskId); -// }; +BackgroundFetch.registerHeadlessTask(task); -// BackgroundFetch.registerHeadlessTask(task); +async function onBackgroundSyncStarted() { + try { + if (!db.isInitialized) { + await db.init(); + } else { + await db.initCollections(); + } -// async function onBackgroundSyncStarted() { -// try { -// DatabaseLogger.info("Background Sync" + "start"); -// await db.init(); -// const user = await db.user?.getUser(); -// if (user) { -// await db.sync(true, false); -// } -// await Notifications.setupReminders(); -// DatabaseLogger.info("Background Sync" + "end"); -// } catch (e) { -// DatabaseLogger.error(e as Error); -// console.log("Background Sync Error", (e as Error).message); -// } -// } + const user = await db.user?.getUser(); + if (user) { + await db.sync(true, false); + } + await Notifications.setupReminders(); + DatabaseLogger.info("BACKGROUND SYNC COMPLETE"); + } catch (e) { + DatabaseLogger.error(e as Error); + console.log("BACKGROUND SYNC ERROR", (e as Error).message); + } +} -// const onBoot = async () => { -// try { -// DatabaseLogger.info("BOOT TASK STARTED"); -// await db.init(); -// await Notifications.setupReminders(); -// SettingsService.init(); -// if (SettingsService.get().notifNotes) { -// Notifications.pinQuickNote(false); -// } -// DatabaseLogger.info("BOOT TASK COMPLETE"); -// } catch (e) { -// console.log(e); -// } -// }; +const onBoot = async () => { + try { + if (!SettingsService.getProperty("backgroundSync")) { + return; + } -// const registerHeadlessTask = () => -// AppRegistry.registerHeadlessTask( -// "com.streetwriters.notesnook.BOOT_TASK", -// () => { -// return onBoot; -// } -// ); + DatabaseLogger.info("BOOT TASK STARTED"); + if (!db.isInitialized) { + await db.init(); + } else { + await db.initCollections(); + } + + await Notifications.setupReminders(); + SettingsService.init(); + if (SettingsService.get().notifNotes) { + Notifications.pinQuickNote(false); + } + DatabaseLogger.info("BOOT TASK COMPLETE"); + } catch (e) { + console.log(e); + } +}; + +const registerHeadlessTask = () => + AppRegistry.registerHeadlessTask( + "com.streetwriters.notesnook.BOOT_TASK", + () => { + return onBoot; + } + ); +export const BackgroundSync = { + start, + registerHeadlessTask +}; -// export const BackgroundSync = { -// start, -// registerHeadlessTask -// }; export default { doInBackground }; diff --git a/apps/mobile/app/stores/use-setting-store.ts b/apps/mobile/app/stores/use-setting-store.ts index 353a66489..b85276398 100644 --- a/apps/mobile/app/stores/use-setting-store.ts +++ b/apps/mobile/app/stores/use-setting-store.ts @@ -79,6 +79,7 @@ export type Settings = { markdownShortcuts?: boolean; appLockHasPasswordSecurity?: boolean; biometricsAuthEnabled?: boolean; + backgroundSync?: boolean; }; type DimensionsType = { diff --git a/apps/mobile/native/android/app/src/main/AndroidManifest.xml b/apps/mobile/native/android/app/src/main/AndroidManifest.xml index 764f03ac7..01ef3534c 100644 --- a/apps/mobile/native/android/app/src/main/AndroidManifest.xml +++ b/apps/mobile/native/android/app/src/main/AndroidManifest.xml @@ -161,12 +161,12 @@ android:resource="@xml/file_viewer_provider_paths" /> - - - - - - + + + + + + diff --git a/apps/mobile/native/index.js b/apps/mobile/native/index.js index c3cfdb995..f5fb76966 100644 --- a/apps/mobile/native/index.js +++ b/apps/mobile/native/index.js @@ -9,6 +9,8 @@ import appJson from './app.json'; import Notifications from "../app/services/notifications"; import NetInfo from "@react-native-community/netinfo"; import { enableFreeze } from "react-native-screens"; +import {BackgroundSync} from '../app/services/background-sync'; + enableFreeze(true); NetInfo.configure({ reachabilityUrl: "https://notesnook.com", @@ -17,7 +19,7 @@ NetInfo.configure({ return response?.status >= 200 && response?.status < 300; } }); - +BackgroundSync.start(); Notifications.init(); const appName = appJson.name; diff --git a/apps/mobile/native/package.json b/apps/mobile/native/package.json index eded284e8..0d33f9df5 100644 --- a/apps/mobile/native/package.json +++ b/apps/mobile/native/package.json @@ -64,7 +64,8 @@ "react-native-webview": "^11.14.1", "react-native-zip-archive": "6.0.9", "react-native-quick-sqlite": "^8.0.6", - "react-native-theme-switch-animation": "^0.6.0" + "react-native-theme-switch-animation": "^0.6.0", + "@ammarahmed/react-native-background-fetch": "^4.2.2" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/apps/mobile/package-lock.json b/apps/mobile/package-lock.json index 0f31b3425..861654dfe 100644 --- a/apps/mobile/package-lock.json +++ b/apps/mobile/package-lock.json @@ -27657,6 +27657,7 @@ "license": "GPL-3.0-or-later", "dependencies": { "@ammarahmed/notifee-react-native": "7.4.4", + "@ammarahmed/react-native-background-fetch": "^4.2.2", "@ammarahmed/react-native-eventsource": "1.1.0", "@ammarahmed/react-native-share-extension": "^2.5.5", "@ammarahmed/react-native-sodium": "1.5.4", @@ -27797,6 +27798,11 @@ "react-native": "*" } }, + "node_modules/@ammarahmed/react-native-background-fetch": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@ammarahmed/react-native-background-fetch/-/react-native-background-fetch-4.2.2.tgz", + "integrity": "sha512-PLBlb/DD+6Fv1auxdD6VRxtuC7GKPhQqR69bovRm5oGavbdSzRr+ZTr8AyNMCcayxKGdgEX2JysrJg8WIySSFQ==" + }, "node_modules/@ammarahmed/react-native-eventsource": { "version": "1.1.0", "license": "MIT", @@ -47661,6 +47667,11 @@ "version": "7.4.4", "requires": {} }, + "@ammarahmed/react-native-background-fetch": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@ammarahmed/react-native-background-fetch/-/react-native-background-fetch-4.2.2.tgz", + "integrity": "sha512-PLBlb/DD+6Fv1auxdD6VRxtuC7GKPhQqR69bovRm5oGavbdSzRr+ZTr8AyNMCcayxKGdgEX2JysrJg8WIySSFQ==" + }, "@ammarahmed/react-native-eventsource": { "version": "1.1.0", "requires": {} @@ -80023,6 +80034,7 @@ "version": "file:native", "requires": { "@ammarahmed/notifee-react-native": "7.4.4", + "@ammarahmed/react-native-background-fetch": "^4.2.2", "@ammarahmed/react-native-eventsource": "1.1.0", "@ammarahmed/react-native-share-extension": "^2.5.5", "@ammarahmed/react-native-sodium": "1.5.4",