mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-29 00:20:04 +01:00
256 lines
6.9 KiB
JavaScript
256 lines
6.9 KiB
JavaScript
import { Platform } from "react-native";
|
|
import FileViewer from "react-native-file-viewer";
|
|
import * as ScopedStorage from "react-native-scoped-storage";
|
|
import Share from "react-native-share";
|
|
import RNFetchBlob from "rn-fetch-blob";
|
|
import { presentDialog } from "../components/dialog/functions";
|
|
import { db } from "../common/database";
|
|
import storage from "../common/database/storage";
|
|
import { eCloseProgressDialog } from "../utils/events";
|
|
import { sanitizeFilename } from "../utils/sanitizer";
|
|
import { sleep } from "../utils/time";
|
|
import { eSendEvent, presentSheet, ToastEvent } from "./event-manager";
|
|
import SettingsService from "./settings";
|
|
|
|
const MS_DAY = 86400000;
|
|
const MS_WEEK = MS_DAY * 7;
|
|
const MONTH = MS_DAY * 30;
|
|
|
|
async function getDirectoryAndroid() {
|
|
let folder = await ScopedStorage.openDocumentTree(true);
|
|
if (!folder) return null;
|
|
let subfolder;
|
|
if (!folder.name.includes("Notesnook backups")) {
|
|
let folderFiles = await ScopedStorage.listFiles(folder.uri);
|
|
for (let f of folderFiles) {
|
|
if (f.type === "directory" && f.name === "Notesnook backups") {
|
|
console.log("folder already exists. reusing");
|
|
subfolder = f;
|
|
}
|
|
}
|
|
if (!subfolder) {
|
|
subfolder = await ScopedStorage.createDirectory(
|
|
folder.uri,
|
|
"Notesnook backups"
|
|
);
|
|
}
|
|
} else {
|
|
subfolder = folder;
|
|
}
|
|
SettingsService.set({
|
|
backupDirectoryAndroid: subfolder
|
|
});
|
|
return subfolder;
|
|
}
|
|
|
|
async function checkBackupDirExists(reset = false, context = "global") {
|
|
if (Platform.OS === "ios") return true;
|
|
let dir = SettingsService.get().backupDirectoryAndroid;
|
|
if (reset) dir = null;
|
|
if (dir) {
|
|
let allDirs = await ScopedStorage.getPersistedUriPermissions();
|
|
let exists = allDirs.findIndex((d) => {
|
|
return d === dir.uri || dir.uri.includes(d);
|
|
});
|
|
exists = exists !== -1;
|
|
dir = exists ? dir : null;
|
|
}
|
|
if (!dir) {
|
|
// eslint-disable-next-line no-async-promise-executor
|
|
dir = await new Promise(async (resolve) => {
|
|
if (reset) {
|
|
resolve(await getDirectoryAndroid());
|
|
return;
|
|
}
|
|
presentDialog({
|
|
title: "Select backup folder",
|
|
paragraph:
|
|
"Please select a folder where you would like to store backup files.",
|
|
positivePress: async () => {
|
|
resolve(await getDirectoryAndroid());
|
|
},
|
|
onClose: () => {
|
|
resolve(null);
|
|
},
|
|
positiveText: "Select",
|
|
context: context
|
|
});
|
|
});
|
|
}
|
|
|
|
return dir;
|
|
}
|
|
|
|
async function presentBackupCompleteSheet(backupFilePath) {
|
|
presentSheet({
|
|
title: "Backup complete",
|
|
icon: "cloud-upload",
|
|
paragraph: `${
|
|
Platform.OS === "android"
|
|
? "Backup file saved in \"Notesnook backups\" folder on your phone"
|
|
: "Backup file is saved in File Manager/Notesnook folder"
|
|
}. Share your backup to your cloud so you do not lose it.`,
|
|
actionText: "Share backup",
|
|
actionsArray: [
|
|
{
|
|
action: () => {
|
|
if (Platform.OS === "ios") {
|
|
console.log(backupFilePath);
|
|
Share.open({
|
|
url: "file:/" + backupFilePath,
|
|
failOnCancel: false
|
|
}).catch(console.log);
|
|
} else {
|
|
FileViewer.open(backupFilePath, {
|
|
showOpenWithDialog: true,
|
|
showAppsSuggestions: true,
|
|
shareFile: true
|
|
}).catch(console.log);
|
|
}
|
|
},
|
|
actionText: "Share"
|
|
},
|
|
{
|
|
action: async () => {
|
|
eSendEvent(eCloseProgressDialog);
|
|
SettingsService.set({
|
|
showBackupCompleteSheet: false
|
|
});
|
|
},
|
|
actionText: "Never ask again",
|
|
type: "grayBg"
|
|
}
|
|
]
|
|
});
|
|
}
|
|
|
|
async function updateNextBackupTime() {
|
|
SettingsService.set({
|
|
nextBackupRequestTime: Date.now() + 86400000 * 3,
|
|
lastBackupDate: Date.now()
|
|
});
|
|
}
|
|
|
|
async function run(progress, context) {
|
|
let androidBackupDirectory = await checkBackupDirExists(false, context);
|
|
if (!androidBackupDirectory) return;
|
|
|
|
let backup;
|
|
|
|
if (progress) {
|
|
presentSheet({
|
|
title: "Backing up your data",
|
|
paragraph:
|
|
"All your backups are stored in 'Phone Storage/Notesnook/backups/' folder",
|
|
progress: true
|
|
});
|
|
}
|
|
|
|
try {
|
|
backup = await db.backup.export(
|
|
"mobile",
|
|
SettingsService.get().encryptedBackup
|
|
);
|
|
if (!backup) throw new Error("Backup returned empty.");
|
|
} catch (e) {
|
|
await sleep(300);
|
|
eSendEvent(eCloseProgressDialog);
|
|
ToastEvent.error(e, "Backup failed!");
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
let backupName = "notesnook_backup_" + Date.now();
|
|
backupName =
|
|
sanitizeFilename(backupName, { replacement: "_" }) + ".nnbackup";
|
|
let path;
|
|
let backupFilePath;
|
|
|
|
if (Platform.OS === "ios") {
|
|
path = await storage.checkAndCreateDir("/backups/");
|
|
await RNFetchBlob.fs.writeFile(path + backupName, backup, "utf8");
|
|
backupFilePath = path + backupName;
|
|
} else {
|
|
backupFilePath = await ScopedStorage.writeFile(
|
|
androidBackupDirectory.uri,
|
|
backup,
|
|
backupName,
|
|
"nnbackup/json",
|
|
"utf8",
|
|
false
|
|
);
|
|
}
|
|
|
|
updateNextBackupTime();
|
|
|
|
ToastEvent.show({
|
|
heading: "Backup successful",
|
|
message: "Your backup is stored in Notesnook folder on your phone.",
|
|
type: "success",
|
|
context: "global"
|
|
});
|
|
|
|
let showBackupCompleteSheet = SettingsService.get().showBackupCompleteSheet;
|
|
console.log(backupFilePath);
|
|
if (context) return backupFilePath;
|
|
await sleep(300);
|
|
if (showBackupCompleteSheet) {
|
|
presentBackupCompleteSheet(backupFilePath);
|
|
} else {
|
|
progress && eSendEvent(eCloseProgressDialog);
|
|
}
|
|
return backupFilePath;
|
|
} catch (e) {
|
|
progress && eSendEvent(eCloseProgressDialog);
|
|
ToastEvent.error(e, "Backup failed!");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function getLastBackupDate() {
|
|
return SettingsService.get().lastBackupDate;
|
|
}
|
|
|
|
async function checkBackupRequired(type) {
|
|
if (type === "off" || type === "useroff") return;
|
|
let now = Date.now();
|
|
let lastBackupDate = await getLastBackupDate();
|
|
if (!lastBackupDate || lastBackupDate === "never") {
|
|
return true;
|
|
}
|
|
lastBackupDate = parseInt(lastBackupDate);
|
|
if (type === "daily" && lastBackupDate + MS_DAY < now) {
|
|
console.log("daily backup started");
|
|
return true;
|
|
} else if (type === "weekly" && lastBackupDate + MS_WEEK < now) {
|
|
console.log("weekly backup started");
|
|
return true;
|
|
} else if (type === "monthly" && lastBackupDate + MONTH < now) {
|
|
console.log("monthly backup started");
|
|
return true;
|
|
}
|
|
console.log("no need", lastBackupDate);
|
|
return false;
|
|
}
|
|
|
|
const checkAndRun = async () => {
|
|
let settings = SettingsService.get();
|
|
if (await checkBackupRequired(settings.reminder)) {
|
|
try {
|
|
await run();
|
|
} catch (e) {
|
|
console.log(e);
|
|
}
|
|
}
|
|
};
|
|
|
|
const BackupService = {
|
|
checkBackupRequired,
|
|
run,
|
|
checkAndRun,
|
|
getDirectoryAndroid,
|
|
checkBackupDirExists
|
|
};
|
|
|
|
export default BackupService;
|