2020-09-15 11:10:16 +00:00
|
|
|
<template>
|
2020-10-22 18:36:50 +00:00
|
|
|
<Page @loaded="initializePage">
|
2020-10-14 19:32:32 +00:00
|
|
|
<ActionBar :flat="viewIsScrolled ? false : true">
|
2020-11-02 11:36:53 +00:00
|
|
|
<GridLayout rows="*" columns="auto, *">
|
2020-10-14 19:32:32 +00:00
|
|
|
<Label
|
2020-11-02 11:36:53 +00:00
|
|
|
class="bx"
|
2020-10-14 19:32:32 +00:00
|
|
|
:text="icon.menu"
|
2020-11-02 11:36:53 +00:00
|
|
|
automationText="Back"
|
2020-10-14 19:32:32 +00:00
|
|
|
@tap="showDrawer"
|
|
|
|
col="0"
|
|
|
|
/>
|
2020-10-21 17:54:45 +00:00
|
|
|
<Label class="title orkm" text="Settings" col="1" />
|
2020-10-14 19:32:32 +00:00
|
|
|
</GridLayout>
|
|
|
|
</ActionBar>
|
2020-11-02 11:36:53 +00:00
|
|
|
<ScrollView scrollBarIndicatorVisible="false" @scroll="onScroll">
|
2020-10-14 19:32:32 +00:00
|
|
|
<StackLayout class="main-container">
|
|
|
|
<Label text="Interface" class="group-header" />
|
|
|
|
<StackLayout
|
|
|
|
orientation="horizontal"
|
|
|
|
class="option"
|
|
|
|
@tap="selectThemes"
|
|
|
|
>
|
|
|
|
<Label verticalAlignment="center" class="bx" :text="icon.theme" />
|
|
|
|
<StackLayout>
|
2020-11-06 09:07:41 +00:00
|
|
|
<Label text="Theme" />
|
2020-11-03 19:57:31 +00:00
|
|
|
<Label :text="appTheme" class="option-info" textWrap="true" />
|
2020-10-14 19:32:32 +00:00
|
|
|
</StackLayout>
|
|
|
|
</StackLayout>
|
|
|
|
<StackLayout class="hr m-10"></StackLayout>
|
2020-11-06 09:07:41 +00:00
|
|
|
<Label text="Database" class="group-header" />
|
2020-11-03 19:57:31 +00:00
|
|
|
<StackLayout orientation="horizontal" class="option" @tap="backupCheck">
|
2020-11-06 09:07:41 +00:00
|
|
|
<Label class="bx" :text="icon.export" />
|
|
|
|
<StackLayout>
|
|
|
|
<Label text="Export a full backup" />
|
|
|
|
<GridLayout
|
|
|
|
class="progressContainer"
|
|
|
|
v-if="backupInProgress"
|
|
|
|
columns="*, 64"
|
|
|
|
>
|
|
|
|
<Progress col="0" :value="backupProgress" />
|
|
|
|
<Label col="1" :text="` ${backupProgress}%`" />
|
|
|
|
</GridLayout>
|
|
|
|
<Label
|
|
|
|
v-else
|
|
|
|
text="Generates a zip file that contains all your data. This file can be imported back."
|
|
|
|
class="option-info"
|
|
|
|
textWrap="true"
|
|
|
|
/>
|
|
|
|
</StackLayout>
|
2020-10-14 19:32:32 +00:00
|
|
|
</StackLayout>
|
2020-11-03 19:57:31 +00:00
|
|
|
<StackLayout
|
|
|
|
orientation="horizontal"
|
|
|
|
class="option"
|
|
|
|
@tap="restoreCheck"
|
|
|
|
>
|
2020-11-06 09:07:41 +00:00
|
|
|
<Label class="bx" :text="icon.import" />
|
|
|
|
<StackLayout>
|
|
|
|
<Label text="Import from backup" />
|
|
|
|
<Label
|
|
|
|
text="Supports full backups exported by this app."
|
|
|
|
class="option-info"
|
|
|
|
textWrap="true"
|
|
|
|
/>
|
|
|
|
</StackLayout>
|
2020-10-14 19:32:32 +00:00
|
|
|
</StackLayout>
|
2020-09-15 11:10:16 +00:00
|
|
|
</StackLayout>
|
2020-10-14 19:32:32 +00:00
|
|
|
</ScrollView>
|
|
|
|
</Page>
|
2020-09-15 11:10:16 +00:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2020-10-24 18:02:35 +00:00
|
|
|
import {
|
|
|
|
ApplicationSettings,
|
|
|
|
path,
|
|
|
|
getFileAccess,
|
|
|
|
knownFolders,
|
2020-11-03 19:57:31 +00:00
|
|
|
Application,
|
|
|
|
File,
|
|
|
|
Folder,
|
2020-10-24 18:02:35 +00:00
|
|
|
} from "@nativescript/core"
|
2020-11-03 19:57:31 +00:00
|
|
|
import * as Permissions from "@nativescript-community/perms"
|
|
|
|
import { Zip } from "@nativescript/zip"
|
2020-10-26 20:49:54 +00:00
|
|
|
import * as Toast from "nativescript-toast"
|
2020-11-03 19:57:31 +00:00
|
|
|
import * as Filepicker from "nativescript-plugin-filepicker"
|
2020-10-14 19:32:32 +00:00
|
|
|
import Theme from "@nativescript/theme"
|
2020-10-21 17:54:45 +00:00
|
|
|
import ActionDialog from "./modal/ActionDialog.vue"
|
|
|
|
import ConfirmDialog from "./modal/ConfirmDialog.vue"
|
2020-10-14 19:32:32 +00:00
|
|
|
|
2020-11-03 19:57:31 +00:00
|
|
|
import { Couchbase } from "nativescript-couchbase-plugin"
|
2020-10-14 19:32:32 +00:00
|
|
|
import { mapState, mapActions } from "vuex"
|
2020-09-15 11:10:16 +00:00
|
|
|
export default {
|
2020-10-21 17:54:45 +00:00
|
|
|
props: [
|
|
|
|
"highlight",
|
|
|
|
"showDrawer",
|
|
|
|
"restartApp",
|
|
|
|
"hijackGlobalBackEvent",
|
|
|
|
"releaseGlobalBackEvent",
|
2020-11-06 09:07:41 +00:00
|
|
|
"openAppSettingsPage",
|
2020-10-21 17:54:45 +00:00
|
|
|
],
|
2020-09-15 11:10:16 +00:00
|
|
|
data() {
|
|
|
|
return {
|
2020-11-02 11:36:53 +00:00
|
|
|
viewIsScrolled: false,
|
2020-11-03 19:57:31 +00:00
|
|
|
appTheme: "Light",
|
2020-11-06 09:07:41 +00:00
|
|
|
backupProgress: 0,
|
|
|
|
backupInProgress: false,
|
2020-09-15 11:10:16 +00:00
|
|
|
}
|
|
|
|
},
|
2020-10-14 19:32:32 +00:00
|
|
|
computed: {
|
2020-11-03 19:57:31 +00:00
|
|
|
...mapState([
|
|
|
|
"icon",
|
|
|
|
"recipes",
|
|
|
|
"userCategories",
|
|
|
|
"userYieldUnits",
|
|
|
|
"currentComponent",
|
|
|
|
]),
|
2020-10-14 19:32:32 +00:00
|
|
|
},
|
2020-09-15 11:10:16 +00:00
|
|
|
methods: {
|
2020-11-06 09:07:41 +00:00
|
|
|
...mapActions([
|
|
|
|
"setCurrentComponentAction",
|
|
|
|
"importCategoriesAction",
|
|
|
|
"importYieldUnitsAction",
|
|
|
|
"importRecipesAction",
|
|
|
|
]),
|
2020-10-22 18:36:50 +00:00
|
|
|
initializePage() {
|
|
|
|
this.setCurrentComponentAction("Settings")
|
2020-10-21 17:54:45 +00:00
|
|
|
this.releaseGlobalBackEvent()
|
|
|
|
},
|
2020-11-02 11:36:53 +00:00
|
|
|
onScroll(args) {
|
|
|
|
args.scrollY
|
|
|
|
? (this.viewIsScrolled = true)
|
|
|
|
: (this.viewIsScrolled = false)
|
2020-10-21 17:54:45 +00:00
|
|
|
},
|
2020-09-15 11:10:16 +00:00
|
|
|
selectThemes(args) {
|
2020-09-15 18:04:33 +00:00
|
|
|
this.highlight(args)
|
2020-10-21 17:54:45 +00:00
|
|
|
this.$showModal(ActionDialog, {
|
|
|
|
props: {
|
|
|
|
title: "Theme",
|
|
|
|
list: ["Light", "Dark"],
|
2020-11-02 11:36:53 +00:00
|
|
|
height: "108",
|
2020-10-21 17:54:45 +00:00
|
|
|
},
|
|
|
|
}).then((action) => {
|
2020-11-03 19:57:31 +00:00
|
|
|
if (action && action !== "Cancel" && this.appTheme !== action) {
|
2020-10-21 17:54:45 +00:00
|
|
|
this.$showModal(ConfirmDialog, {
|
|
|
|
props: {
|
2020-11-06 09:07:41 +00:00
|
|
|
title: "Reload required",
|
2020-10-21 17:54:45 +00:00
|
|
|
description:
|
2020-11-06 09:07:41 +00:00
|
|
|
"EnRecipes needs to be reloaded for the theme change to take effect.",
|
2020-10-21 17:54:45 +00:00
|
|
|
cancelButtonText: "CANCEL",
|
|
|
|
okButtonText: "RELOAD",
|
|
|
|
},
|
|
|
|
}).then((result) => {
|
|
|
|
if (result) {
|
2020-11-03 19:57:31 +00:00
|
|
|
this.appTheme = action
|
|
|
|
ApplicationSettings.setString("appTheme", action)
|
2020-10-21 17:54:45 +00:00
|
|
|
setTimeout((e) => this.restartApp(), 250)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2020-09-15 11:10:16 +00:00
|
|
|
})
|
|
|
|
},
|
2020-11-03 19:57:31 +00:00
|
|
|
|
|
|
|
writeFile(file, data) {
|
2020-11-06 09:07:41 +00:00
|
|
|
file.writeText(JSON.stringify(data))
|
2020-09-15 11:10:16 +00:00
|
|
|
},
|
2020-11-03 19:57:31 +00:00
|
|
|
BackupDataFiles(option) {
|
|
|
|
const folder = path.join(knownFolders.documents().path, "EnRecipes")
|
|
|
|
const EnRecipesFile = File.fromPath(path.join(folder, "EnRecipes.json"))
|
2020-11-06 09:07:41 +00:00
|
|
|
let userCategoriesFile, userYieldUnitsFile
|
|
|
|
if (this.userCategories.length) {
|
|
|
|
userCategoriesFile = File.fromPath(
|
|
|
|
path.join(folder, "userCategories.json")
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (this.userYieldUnits.length) {
|
|
|
|
userYieldUnitsFile = File.fromPath(
|
|
|
|
path.join(folder, "userYieldUnits.json")
|
|
|
|
)
|
|
|
|
}
|
2020-11-03 19:57:31 +00:00
|
|
|
switch (option) {
|
|
|
|
case "create":
|
|
|
|
this.writeFile(EnRecipesFile, this.recipes)
|
|
|
|
this.userCategories.length &&
|
|
|
|
this.writeFile(userCategoriesFile, this.userCategories)
|
|
|
|
this.userYieldUnits.length &&
|
|
|
|
this.writeFile(userYieldUnitsFile, this.userYieldUnits)
|
|
|
|
break
|
|
|
|
case "delete":
|
|
|
|
EnRecipesFile.remove()
|
|
|
|
this.userCategories.length && userCategoriesFile.remove()
|
|
|
|
this.userYieldUnits.length && userYieldUnitsFile.remove()
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
},
|
|
|
|
backupPermissionConfirmation() {
|
|
|
|
return this.$showModal(ConfirmDialog, {
|
|
|
|
props: {
|
|
|
|
title: "Grant permission",
|
|
|
|
description:
|
|
|
|
"EnRecipes requires storage permission in order to backup your data to this device",
|
|
|
|
cancelButtonText: "NOT NOW",
|
|
|
|
okButtonText: "CONTINUE",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
},
|
|
|
|
backupData() {
|
|
|
|
this.BackupDataFiles("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 = android.os.Environment.getExternalStoragePublicDirectory(
|
|
|
|
android.os.Environment.DIRECTORY_DOWNLOADS
|
|
|
|
).toString()
|
|
|
|
let fromPath = path.join(knownFolders.documents().path, "EnRecipes")
|
|
|
|
let destPath = path.join(
|
|
|
|
sdDownloadPath,
|
|
|
|
`EnRecipes-Backup_${formattedDate}.zip`
|
|
|
|
)
|
2020-11-06 09:07:41 +00:00
|
|
|
this.backupInProgress = true
|
2020-11-03 19:57:31 +00:00
|
|
|
Zip.zip({
|
|
|
|
directory: fromPath,
|
|
|
|
archive: destPath,
|
2020-11-06 09:07:41 +00:00
|
|
|
onProgress: (progress) => {
|
|
|
|
this.backupProgress = progress
|
|
|
|
if (progress == 100) {
|
|
|
|
setTimeout((e) => {
|
|
|
|
this.backupInProgress = false
|
|
|
|
}, 2000)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}).then((success) => {
|
|
|
|
Toast.makeText(
|
|
|
|
"Backup file successfully saved to Downloads",
|
|
|
|
"long"
|
|
|
|
).show()
|
|
|
|
this.BackupDataFiles("delete")
|
2020-11-03 19:57:31 +00:00
|
|
|
})
|
2020-09-15 11:10:16 +00:00
|
|
|
},
|
2020-11-03 19:57:31 +00:00
|
|
|
backupCheck(args) {
|
|
|
|
let btn = args.object
|
|
|
|
this.highlight(args)
|
|
|
|
if (!this.recipes.length) {
|
2020-11-06 09:07:41 +00:00
|
|
|
Toast.makeText(
|
|
|
|
"Add at least one recipe to perform a backup",
|
|
|
|
"long"
|
|
|
|
).show()
|
2020-11-03 19:57:31 +00:00
|
|
|
} else {
|
|
|
|
this.permissionCheck(this.backupPermissionConfirmation, this.backupData)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
restorePermissionConfirmation() {
|
|
|
|
return this.$showModal(ConfirmDialog, {
|
|
|
|
props: {
|
|
|
|
title: "Grant permission",
|
|
|
|
description:
|
|
|
|
"EnRecipes requires storage permission in order to restore your data from a previous backup.",
|
|
|
|
cancelButtonText: "NOT NOW",
|
|
|
|
okButtonText: "CONTINUE",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
},
|
|
|
|
restoreCheck(args) {
|
2020-09-15 11:10:16 +00:00
|
|
|
let btn = args.object
|
2020-09-15 18:04:33 +00:00
|
|
|
this.highlight(args)
|
2020-11-03 19:57:31 +00:00
|
|
|
|
|
|
|
this.permissionCheck(
|
|
|
|
this.restorePermissionConfirmation,
|
|
|
|
this.openFilePicker
|
|
|
|
)
|
|
|
|
},
|
|
|
|
openFilePicker() {
|
|
|
|
let context = Filepicker.create({
|
2020-10-26 20:49:54 +00:00
|
|
|
mode: "single", // use "multiple" for multiple selection
|
|
|
|
extensions: ["zip"],
|
|
|
|
})
|
2020-11-03 19:57:31 +00:00
|
|
|
context.present().then((selection) => {
|
|
|
|
Toast.makeText("Processing...").show()
|
|
|
|
let result = selection[0]
|
|
|
|
let zipPath = result
|
|
|
|
let dest = knownFolders.documents().path
|
|
|
|
this.validateZipContent(zipPath)
|
2020-11-06 09:07:41 +00:00
|
|
|
})
|
|
|
|
},
|
|
|
|
importDataToDB(data, db, zipPath) {
|
|
|
|
switch (db) {
|
|
|
|
case "EnRecipesDB":
|
|
|
|
this.copyImages(zipPath)
|
|
|
|
this.importRecipesAction(data)
|
|
|
|
break
|
|
|
|
case "userCategoriesDB":
|
|
|
|
this.importCategoriesAction(data)
|
|
|
|
break
|
|
|
|
case "userYieldUnitsDB":
|
|
|
|
this.importYieldUnitsAction(data)
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
},
|
|
|
|
isImportedDataValid(file) {
|
|
|
|
file.forEach((file, i) => {
|
|
|
|
if (File.exists(file.path)) {
|
|
|
|
File.fromPath(file.path)
|
|
|
|
.readText()
|
|
|
|
.then((data) => {
|
|
|
|
Array.isArray(JSON.parse(data)) &&
|
|
|
|
this.importDataToDB(JSON.parse(data), file.db, file.zipPath)
|
|
|
|
})
|
|
|
|
}
|
2020-11-03 19:57:31 +00:00
|
|
|
})
|
|
|
|
},
|
|
|
|
validateZipContent(zipPath) {
|
|
|
|
Zip.unzip({
|
|
|
|
archive: zipPath,
|
|
|
|
overwrite: true,
|
2020-11-06 09:07:41 +00:00
|
|
|
}).then((extractedFolderPath) => {
|
|
|
|
let cacheFolderPath = extractedFolderPath + "/EnRecipes"
|
2020-11-03 19:57:31 +00:00
|
|
|
const EnRecipesFilePath = cacheFolderPath + "/EnRecipes.json"
|
|
|
|
const userCategoriesFilePath = cacheFolderPath + "/userCategories.json"
|
|
|
|
const userYieldUnitsFilePath = cacheFolderPath + "/userYieldUnits.json"
|
2020-11-06 09:07:41 +00:00
|
|
|
if (Folder.exists(cacheFolderPath)) {
|
|
|
|
this.isImportedDataValid([
|
|
|
|
{
|
|
|
|
zipPath,
|
|
|
|
path: EnRecipesFilePath,
|
|
|
|
db: "EnRecipesDB",
|
|
|
|
},
|
|
|
|
{ zipPath, path: userCategoriesFilePath, db: "userCategoriesDB" },
|
|
|
|
{ zipPath, path: userYieldUnitsFilePath, db: "userYieldUnitsDB" },
|
|
|
|
])
|
2020-11-03 19:57:31 +00:00
|
|
|
} else {
|
2020-11-06 09:07:41 +00:00
|
|
|
Folder.fromPath(extractedFolderPath).remove()
|
|
|
|
Toast.makeText(
|
|
|
|
"Zip modified externally or incorrect file",
|
|
|
|
"long"
|
|
|
|
).show()
|
|
|
|
}
|
|
|
|
if (Folder.exists(cacheFolderPath + "/Images")) {
|
|
|
|
this.copyImages(cacheFolderPath + "/Images")
|
2020-11-03 19:57:31 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
2020-11-06 09:07:41 +00:00
|
|
|
copyImages(sourcePath) {
|
|
|
|
let dest = knownFolders.documents().path
|
|
|
|
Zip.unzip({
|
|
|
|
archive: sourcePath,
|
|
|
|
directory: dest,
|
|
|
|
overwrite: true,
|
|
|
|
}).then((res) => {
|
|
|
|
Toast.makeText("Import successful!", "long").show()
|
|
|
|
})
|
2020-11-03 19:57:31 +00:00
|
|
|
},
|
|
|
|
permissionCheck(confirmation, action) {
|
|
|
|
if (!ApplicationSettings.getBoolean("storagePermissionAsked", false)) {
|
|
|
|
confirmation().then((e) => {
|
|
|
|
if (e) {
|
|
|
|
Permissions.request("storage").then((res) => {
|
|
|
|
let status = res[Object.keys(res)[0]]
|
|
|
|
if (status === "authorized") action()
|
|
|
|
if (status !== "denied")
|
|
|
|
ApplicationSettings.setBoolean("storagePermissionAsked", true)
|
|
|
|
else Toast.makeText("Permission denied").show()
|
|
|
|
})
|
|
|
|
}
|
2020-10-26 20:49:54 +00:00
|
|
|
})
|
2020-11-03 19:57:31 +00:00
|
|
|
} else {
|
|
|
|
Permissions.request("storage").then((res) => {
|
|
|
|
let status = res[Object.keys(res)[0]]
|
|
|
|
if (status !== "authorized") {
|
|
|
|
confirmation().then((e) => {
|
|
|
|
e && this.openAppSettingsPage()
|
|
|
|
})
|
|
|
|
} else action()
|
2020-09-15 11:10:16 +00:00
|
|
|
})
|
2020-11-03 19:57:31 +00:00
|
|
|
}
|
|
|
|
},
|
2020-09-15 11:10:16 +00:00
|
|
|
},
|
2020-10-14 19:32:32 +00:00
|
|
|
created() {
|
2020-11-03 19:57:31 +00:00
|
|
|
this.appTheme = ApplicationSettings.getString("appTheme", "Light")
|
2020-10-14 19:32:32 +00:00
|
|
|
},
|
2020-09-15 11:10:16 +00:00
|
|
|
}
|
|
|
|
</script>
|