2021-03-21 17:02:04 +00:00
|
|
|
<template>
|
2021-04-01 10:55:35 +00:00
|
|
|
<Page @loaded="onPageLoad" actionBarHidden="true">
|
|
|
|
<GridLayout rows="*, auto" columns="auto, *">
|
|
|
|
<ListView
|
|
|
|
colSpan="2"
|
|
|
|
rowSpan="2"
|
|
|
|
class="options-list"
|
|
|
|
@loaded="listViewLoad"
|
|
|
|
for="item in items"
|
|
|
|
>
|
|
|
|
<v-template if="$index == 0">
|
|
|
|
<Label class="pageTitle" :text="'db' | L" />
|
|
|
|
</v-template>
|
|
|
|
<v-template if="$index == 3">
|
|
|
|
<StackLayout class="listSpace"> </StackLayout>
|
|
|
|
</v-template>
|
|
|
|
<v-template>
|
|
|
|
<GridLayout
|
|
|
|
columns="auto, *"
|
|
|
|
class="option"
|
|
|
|
@touch="touch($event, item.action)"
|
|
|
|
>
|
|
|
|
<Label class="ico" :text="icon[item.icon]" />
|
|
|
|
<StackLayout col="1">
|
|
|
|
<Label :text="item.title | L" class="info" />
|
|
|
|
<Label :text="item.subTitle | L" class="sub" />
|
|
|
|
</StackLayout>
|
|
|
|
</GridLayout>
|
|
|
|
</v-template>
|
|
|
|
</ListView>
|
|
|
|
<GridLayout row="1" class="appbar" rows="*" columns="auto, *">
|
|
|
|
<Button
|
|
|
|
class="ico"
|
2021-03-21 17:02:04 +00:00
|
|
|
:text="icon.back"
|
|
|
|
@tap="$navigateBack()"
|
|
|
|
/>
|
|
|
|
</GridLayout>
|
2021-04-01 10:55:35 +00:00
|
|
|
</GridLayout>
|
2021-03-21 17:02:04 +00:00
|
|
|
</Page>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
import {
|
|
|
|
ApplicationSettings,
|
|
|
|
path,
|
|
|
|
knownFolders,
|
|
|
|
File,
|
|
|
|
Folder,
|
|
|
|
Observable,
|
|
|
|
} from "@nativescript/core";
|
|
|
|
import * as Permissions from "@nativescript-community/perms";
|
|
|
|
import { Zip } from "@nativescript/zip";
|
2021-04-01 13:29:26 +00:00
|
|
|
// import * as Filepicker from "nativescript-plugin-filepicker";
|
2021-03-21 17:02:04 +00:00
|
|
|
import { localize } from "@nativescript/localize";
|
|
|
|
import ConfirmDialog from "../modal/ConfirmDialog.vue";
|
|
|
|
import { mapState, mapActions } from "vuex";
|
|
|
|
|
|
|
|
import * as utils from "~/shared/utils";
|
|
|
|
|
|
|
|
export default {
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
backupProgress: 0,
|
|
|
|
backupInProgress: false,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
...mapState([
|
|
|
|
"icon",
|
|
|
|
"recipes",
|
|
|
|
"cuisines",
|
|
|
|
"categories",
|
|
|
|
"yieldUnits",
|
|
|
|
"units",
|
|
|
|
"mealPlans",
|
|
|
|
"importSummary",
|
|
|
|
]),
|
2021-04-01 10:55:35 +00:00
|
|
|
items() {
|
|
|
|
return [
|
|
|
|
{},
|
|
|
|
{
|
|
|
|
icon: "exp",
|
|
|
|
title: "expBu",
|
|
|
|
subTitle: "buInfo",
|
|
|
|
action: this.exportCheck,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
icon: "imp",
|
|
|
|
title: "impBu",
|
|
|
|
subTitle: "impInfo",
|
|
|
|
action: this.importCheck,
|
|
|
|
},
|
|
|
|
{},
|
|
|
|
];
|
|
|
|
},
|
2021-03-21 17:02:04 +00:00
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
...mapActions([
|
|
|
|
"importListItemsAction",
|
|
|
|
"importRecipesAction",
|
|
|
|
"importMealPlansAction",
|
|
|
|
"unlinkBrokenImages",
|
|
|
|
]),
|
|
|
|
onPageLoad(args) {
|
|
|
|
const page = args.object;
|
|
|
|
page.bindingContext = new Observable();
|
|
|
|
},
|
|
|
|
// EXPORT HANDLERS
|
|
|
|
exportCheck() {
|
|
|
|
if (!this.recipes.length) {
|
2021-04-01 12:23:44 +00:00
|
|
|
// Toast.makeText(localize("aFBu")).show();
|
2021-03-21 17:02:04 +00:00
|
|
|
} else {
|
|
|
|
this.permissionCheck(
|
|
|
|
this.permissionConfirmation,
|
|
|
|
localize("reqAcc"),
|
|
|
|
this.exportBackup
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
exportBackup() {
|
|
|
|
this.exportFiles("create");
|
|
|
|
let date = new Date();
|
|
|
|
let formattedDate =
|
|
|
|
date.getFullYear() +
|
|
|
|
"-" +
|
|
|
|
("0" + (date.getMonth() + 1)).slice(-2) +
|
|
|
|
"-" +
|
|
|
|
("0" + date.getDate()).slice(-2) +
|
|
|
|
"_" +
|
|
|
|
("0" + date.getHours()).slice(-2) +
|
|
|
|
("0" + date.getMinutes()).slice(-2) +
|
|
|
|
("0" + date.getSeconds()).slice(-2);
|
|
|
|
const sdDownloadPath = Folder.fromPath(
|
|
|
|
android.os.Environment.getExternalStorageDirectory().getAbsolutePath()
|
|
|
|
).getFolder("Download").path;
|
|
|
|
let fromPath = path.join(knownFolders.documents().path, "EnRecipes");
|
|
|
|
let destPath = path.join(
|
|
|
|
sdDownloadPath,
|
|
|
|
`EnRecipes-Backup_${formattedDate}.zip`
|
|
|
|
);
|
|
|
|
this.backupInProgress = true;
|
|
|
|
Zip.zip({
|
|
|
|
directory: fromPath,
|
|
|
|
archive: destPath,
|
|
|
|
onProgress: (progress) => {
|
|
|
|
this.backupProgress = progress;
|
|
|
|
},
|
|
|
|
}).then((success) => {
|
2021-04-01 12:23:44 +00:00
|
|
|
// Toast.makeText(
|
|
|
|
// "Backup file successfully saved to Download folder",
|
|
|
|
// "long"
|
|
|
|
// ).show();
|
2021-03-21 17:02:04 +00:00
|
|
|
this.exportFiles("delete");
|
|
|
|
setTimeout((e) => (this.backupInProgress = false), 3000);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
exportFiles(option) {
|
|
|
|
const folder = path.join(knownFolders.documents().path, "EnRecipes");
|
|
|
|
const EnRecipesFile = File.fromPath(path.join(folder, "recipes.json"));
|
|
|
|
let userCuisinesFile,
|
|
|
|
userCategoriesFile,
|
|
|
|
userYieldUnitsFile,
|
|
|
|
userUnitsFile,
|
|
|
|
mealPlansFile;
|
|
|
|
if (this.cuisines.length)
|
|
|
|
userCuisinesFile = File.fromPath(
|
|
|
|
path.join(folder, "userCuisines.json")
|
|
|
|
);
|
|
|
|
if (this.categories.length)
|
|
|
|
userCategoriesFile = File.fromPath(
|
|
|
|
path.join(folder, "userCategories.json")
|
|
|
|
);
|
|
|
|
if (this.yieldUnits.length)
|
|
|
|
userYieldUnitsFile = File.fromPath(
|
|
|
|
path.join(folder, "userYieldUnits.json")
|
|
|
|
);
|
|
|
|
if (this.units.length)
|
|
|
|
userUnitsFile = File.fromPath(path.join(folder, "userUnits.json"));
|
|
|
|
if (this.mealPlans.length)
|
|
|
|
mealPlansFile = File.fromPath(path.join(folder, "mealPlans.json"));
|
|
|
|
switch (option) {
|
|
|
|
case "create":
|
|
|
|
this.writeDataToFile(EnRecipesFile, this.recipes);
|
|
|
|
this.cuisines.length &&
|
|
|
|
this.writeDataToFile(userCuisinesFile, this.cuisines);
|
|
|
|
this.categories.length &&
|
|
|
|
this.writeDataToFile(userCategoriesFile, this.categories);
|
|
|
|
this.yieldUnits.length &&
|
|
|
|
this.writeDataToFile(userYieldUnitsFile, this.yieldUnits);
|
|
|
|
this.units.length && this.writeDataToFile(userUnitsFile, this.units);
|
|
|
|
this.mealPlans.length &&
|
|
|
|
this.writeDataToFile(mealPlansFile, this.mealPlans);
|
|
|
|
break;
|
|
|
|
case "delete":
|
|
|
|
EnRecipesFile.remove();
|
|
|
|
this.cuisines.length && userCuisinesFile.remove();
|
|
|
|
this.categories.length && userCategoriesFile.remove();
|
|
|
|
this.yieldUnits.length && userYieldUnitsFile.remove();
|
|
|
|
this.units.length && userUnitsFile.remove();
|
|
|
|
this.mealPlans.length && mealPlansFile.remove();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
writeDataToFile(file, data) {
|
|
|
|
file.writeText(JSON.stringify(data));
|
|
|
|
},
|
|
|
|
// IMPORT HANDLERS
|
|
|
|
importCheck() {
|
|
|
|
this.permissionCheck(
|
|
|
|
this.permissionConfirmation,
|
|
|
|
localize("reqAcc"),
|
|
|
|
this.openFilePicker
|
|
|
|
);
|
|
|
|
},
|
|
|
|
openFilePicker() {
|
2021-04-01 13:29:26 +00:00
|
|
|
// Filepicker.create({
|
|
|
|
// mode: "single",
|
|
|
|
// extensions: ["zip"],
|
|
|
|
// })
|
|
|
|
// .present()
|
|
|
|
// .then((selection) => {
|
|
|
|
// // Toast.makeText(localize("vrfy") + "...").show();
|
|
|
|
// let zipPath = selection[0];
|
|
|
|
// this.validateZipContent(zipPath);
|
|
|
|
// });
|
2021-03-21 17:02:04 +00:00
|
|
|
},
|
|
|
|
importDataToDB(data, db, zipPath) {
|
|
|
|
switch (db) {
|
|
|
|
case "EnRecipesDB":
|
|
|
|
this.importImages(zipPath);
|
|
|
|
this.importRecipesAction(data);
|
|
|
|
break;
|
|
|
|
case "userCuisinesDB":
|
|
|
|
this.importListItemsAction({
|
|
|
|
data,
|
|
|
|
listName: "cuisines",
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case "userCategoriesDB":
|
|
|
|
this.importListItemsAction({
|
|
|
|
data,
|
|
|
|
listName: "categories",
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case "userYieldUnitsDB":
|
|
|
|
this.importListItemsAction({
|
|
|
|
data,
|
|
|
|
listName: "yieldUnits",
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case "userUnitsDB":
|
|
|
|
this.importListItemsAction({
|
|
|
|
data,
|
|
|
|
listName: "units",
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case "mealPlansDB":
|
|
|
|
this.importMealPlansAction(data);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
hasValidJSON(data) {
|
|
|
|
try {
|
|
|
|
JSON.parse(data) && Array.isArray(JSON.parse(data));
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
isFileDataValid(file) {
|
|
|
|
const files = file.filter((e) => File.exists(e.path));
|
|
|
|
if (files.length) {
|
|
|
|
let isValid = files.map((e) => false);
|
|
|
|
files.forEach((file, i) => {
|
|
|
|
File.fromPath(file.path)
|
|
|
|
.readText()
|
|
|
|
.then((data) => {
|
|
|
|
isValid[i] = this.hasValidJSON(data);
|
|
|
|
if (!isValid[i]) {
|
|
|
|
this.failedImport(
|
|
|
|
`${localize("buMod")}\n\n${localize("invFile")}: ${file.file}`
|
|
|
|
);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (isValid.every((e) => e === true)) {
|
|
|
|
files.forEach((file, i) => {
|
|
|
|
File.fromPath(file.path)
|
|
|
|
.readText()
|
|
|
|
.then((data) => {
|
|
|
|
this.importDataToDB(
|
|
|
|
JSON.parse(data),
|
|
|
|
file.db,
|
|
|
|
file.zipPath
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.failedImport(localize("buEmp"));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
failedImport(description) {
|
|
|
|
this.$showModal(ConfirmDialog, {
|
|
|
|
props: {
|
|
|
|
title: "impFail",
|
|
|
|
description,
|
|
|
|
okButtonText: "OK",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
validateZipContent(zipPath) {
|
|
|
|
Zip.unzip({
|
|
|
|
archive: zipPath,
|
|
|
|
overwrite: true,
|
|
|
|
}).then((extractedFolderPath) => {
|
|
|
|
let cacheFolderPath = extractedFolderPath + "/EnRecipes";
|
|
|
|
const EnRecipesFilePath = cacheFolderPath + "/recipes.json";
|
|
|
|
const userCuisinesFilePath = cacheFolderPath + "/userCuisines.json";
|
|
|
|
const userCategoriesFilePath = cacheFolderPath + "/userCategories.json";
|
|
|
|
const userYieldUnitsFilePath = cacheFolderPath + "/userYieldUnits.json";
|
|
|
|
const userUnitsFilePath = cacheFolderPath + "/userUnits.json";
|
|
|
|
const mealPlansFilePath = cacheFolderPath + "/mealPlans.json";
|
|
|
|
if (Folder.exists(cacheFolderPath)) {
|
|
|
|
this.isFileDataValid([
|
|
|
|
{
|
|
|
|
zipPath,
|
|
|
|
path: EnRecipesFilePath,
|
|
|
|
db: "EnRecipesDB",
|
|
|
|
file: "recipes.json",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
zipPath,
|
|
|
|
path: userCuisinesFilePath,
|
|
|
|
db: "userCuisinesDB",
|
|
|
|
file: "userCuisines.json",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
zipPath,
|
|
|
|
path: userCategoriesFilePath,
|
|
|
|
db: "userCategoriesDB",
|
|
|
|
file: "userCategories.json",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
zipPath,
|
|
|
|
path: userYieldUnitsFilePath,
|
|
|
|
db: "userYieldUnitsDB",
|
|
|
|
file: "userYieldUnits.json",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
zipPath,
|
|
|
|
path: userUnitsFilePath,
|
|
|
|
db: "userUnitsDB",
|
|
|
|
file: "userUnits.json",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
zipPath,
|
|
|
|
path: mealPlansFilePath,
|
|
|
|
db: "mealPlansDB",
|
|
|
|
file: "mealPlans.json",
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
} else {
|
|
|
|
Folder.fromPath(extractedFolderPath).remove();
|
|
|
|
this.failedImport(localize("buInc"));
|
|
|
|
}
|
|
|
|
if (Folder.exists(cacheFolderPath + "/Images")) {
|
|
|
|
this.importImages(cacheFolderPath + "/Images");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
importImages(sourcePath) {
|
|
|
|
let dest = knownFolders.documents().path;
|
|
|
|
Zip.unzip({
|
|
|
|
archive: sourcePath,
|
|
|
|
directory: dest,
|
|
|
|
overwrite: true,
|
|
|
|
}).then((res) => {
|
|
|
|
this.showImportSummary();
|
|
|
|
this.unlinkBrokenImages();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
showImportSummary() {
|
|
|
|
let { found, imported, updated } = this.importSummary;
|
|
|
|
let exists = Math.abs(found - imported - updated) + updated;
|
|
|
|
let importedNote = `\n${imported} ${localize("recI")}`;
|
|
|
|
let existsNote = `\n${exists} ${localize("recE")}`;
|
|
|
|
let updatedNote = `\n${updated} ${localize("recU")}`;
|
|
|
|
this.$showModal(ConfirmDialog, {
|
|
|
|
props: {
|
|
|
|
title: "impSuc",
|
|
|
|
description: `${found} ${localize(
|
|
|
|
"recF"
|
|
|
|
)}${importedNote}${existsNote}${updatedNote}`,
|
|
|
|
okButtonText: "OK",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
// PERMISSIONS HANDLER
|
|
|
|
permissionCheck(confirmation, description, action) {
|
|
|
|
if (!ApplicationSettings.getBoolean("storagePermissionAsked", false)) {
|
|
|
|
confirmation(description).then((e) => {
|
|
|
|
if (e) {
|
|
|
|
Permissions.request("photo").then((res) => {
|
|
|
|
let status = res[Object.keys(res)[0]];
|
|
|
|
if (status === "authorized") action();
|
|
|
|
if (status !== "denied")
|
|
|
|
ApplicationSettings.setBoolean("storagePermissionAsked", true);
|
2021-04-01 12:23:44 +00:00
|
|
|
// else Toast.makeText(localize("dend")).show();
|
2021-03-21 17:02:04 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
Permissions.check("photo").then((res) => {
|
|
|
|
let status = res[Object.keys(res)[0]];
|
|
|
|
if (status !== "authorized") {
|
|
|
|
confirmation(description).then((e) => {
|
|
|
|
e && utils.openAppSettingsPage();
|
|
|
|
});
|
|
|
|
} else action();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
permissionConfirmation(description) {
|
|
|
|
return this.$showModal(ConfirmDialog, {
|
|
|
|
props: {
|
|
|
|
title: "grant",
|
|
|
|
description,
|
|
|
|
cancelButtonText: "nNBtn",
|
|
|
|
okButtonText: "conBtn",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
2021-04-01 10:55:35 +00:00
|
|
|
// HELPERS
|
|
|
|
touch({ object, action }, method) {
|
|
|
|
object.className = action.match(/down|move/) ? "option fade" : "option";
|
|
|
|
if (action == "up") method();
|
|
|
|
},
|
2021-03-21 17:02:04 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|