2020-09-15 11:10:16 +00:00
< template >
2020-12-07 14:45:00 +00:00
< Page @loaded ="onPageLoad" >
< ActionBar : androidElevation = "viewIsScrolled ? 4 : 0" >
< GridLayout rows = "*" columns = "auto, *" >
< MDButton class = "bx left" variant = "text" :text = "icon.menu" automationText = "Back" @tap ="showDrawer" col = "0" / >
< Label class = "title orkm" : text = "'Settings' | L" col = "1" / >
< / GridLayout >
< / ActionBar >
< ScrollView @scroll ="onScroll" >
< StackLayout class = "main-container" >
< Label : text = "'Interface' | L" class = "group-header orkm" / >
< GridLayout columns = "auto, *" class = "option" >
< MDRipple colSpan = "2" @tap ="selectAppLanguage" / >
< Label col = "0" verticalAlignment = "center" class = "bx" :text = "icon.globe" / >
< StackLayout col = "1" >
< Label : text = "'App language' | L" / >
< Label :text = "appLanguage" class = "info" / >
< / StackLayout >
2020-10-14 19:32:32 +00:00
< / GridLayout >
2020-12-07 14:45:00 +00:00
< GridLayout columns = "auto, *" class = "option" >
< MDRipple colSpan = "2" @tap ="selectThemes" / >
< Label col = "0" verticalAlignment = "center" class = "bx" :text = "icon.theme" / >
< StackLayout col = "1" >
< Label : text = "'Theme' | L" / >
< Label : text = "`${appTheme}` | L" class = "info" / >
< / StackLayout >
< / GridLayout >
< StackLayout class = "hr m-10" > < / StackLayout >
< Label : text = "'Database' | L" class = "group-header orkm" / >
< GridLayout columns = "auto, *" class = "option" >
< MDRipple colSpan = "2" @tap ="exportCheck" / >
< Label col = "0" class = "bx" :text = "icon.export" / >
< StackLayout col = "1" >
< Label : text = "'Export a full backup' | L" textWrap = "true" / >
< Label v-if = "!backupInProgress" :text="'Generates a zip file that contains all your data. This file can be imported back.' | L" class="info" textWrap="true" / >
< GridLayout class = "progressContainer" v -else columns = "*, 64" >
< MDProgress col = "0" :value = "backupProgress" maxValue = "100" > < / MDProgress >
< Label col = "1" : text = "` ${backupProgress}%`" / >
< / GridLayout >
< / StackLayout >
< / GridLayout >
< GridLayout columns = "auto, *" class = "option" >
< MDRipple colSpan = "2" @tap ="importCheck" / >
< Label col = "0" class = "bx" :text = "icon.import" / >
< StackLayout col = "1" >
< Label : text = "'Import from backup' | L" textWrap = "true" / >
< Label : text = "'Supports full backups exported by this app' | L" class = "info" textWrap = "true" / >
< / StackLayout >
< / GridLayout >
< / StackLayout >
< / ScrollView >
< / Page >
2020-09-15 11:10:16 +00:00
< / template >
< script >
2020-10-24 18:02:35 +00:00
import {
ApplicationSettings ,
path ,
knownFolders ,
2020-11-03 19:57:31 +00:00
File ,
Folder ,
2020-12-07 14:45:00 +00:00
Observable ,
Device
}
from "@nativescript/core"
2020-11-03 19:57:31 +00:00
import * as Permissions from "@nativescript-community/perms"
2020-12-07 14:45:00 +00:00
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-12-07 14:45:00 +00:00
import {
localize ,
overrideLocale
}
from "@nativescript/localize"
import {
mapState ,
mapActions
}
from "vuex"
2020-11-10 18:28:48 +00:00
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-23 09:49:58 +00:00
import * as utils from "~/shared/utils"
2020-09-15 11:10:16 +00:00
export default {
data ( ) {
return {
2020-11-02 11:36:53 +00:00
viewIsScrolled : false ,
2020-11-03 19:57:31 +00:00
appTheme : "Light" ,
2020-12-07 14:45:00 +00:00
appLanguage : "English" ,
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-12-07 14:45:00 +00:00
... mapState ( [
2020-11-03 19:57:31 +00:00
"icon" ,
"recipes" ,
"userCategories" ,
"userYieldUnits" ,
2020-11-23 09:49:58 +00:00
"mealPlans" ,
2020-11-03 19:57:31 +00:00
"currentComponent" ,
2020-12-07 14:45:00 +00:00
"language" ,
] ) ,
2020-10-14 19:32:32 +00:00
} ,
2020-09-15 11:10:16 +00:00
methods : {
2020-12-07 14:45:00 +00:00
... mapActions ( [
2020-11-06 09:07:41 +00:00
"setCurrentComponentAction" ,
"importCategoriesAction" ,
"importYieldUnitsAction" ,
"importRecipesAction" ,
2020-11-23 09:49:58 +00:00
"importMealPlansAction" ,
2020-12-07 14:45:00 +00:00
] ) ,
onPageLoad ( args ) {
const page = args . object ;
page . bindingContext = new Observable ( ) ;
this . setCurrentComponentAction ( "Settings" )
2020-10-21 17:54:45 +00:00
} ,
2020-11-10 18:28:48 +00:00
// HELPERS
2020-11-23 09:49:58 +00:00
showDrawer ( ) {
utils . showDrawer ( )
} ,
2020-12-07 14:45:00 +00:00
onScroll ( args ) {
2020-11-28 19:21:57 +00:00
this . viewIsScrolled = args . scrollY ? true : false
2020-10-21 17:54:45 +00:00
} ,
2020-11-10 18:28:48 +00:00
2020-12-07 14:45:00 +00:00
// LANGUAGE SELECTION
selectAppLanguage ( ) {
let languages = this . language . map ( e => e . title )
this . $showModal ( ActionDialog , {
props : {
title : "App language" ,
list : [ ... languages ] ,
} ,
} ) . then ( ( action ) => {
if ( action && action !== "Cancel" && this . appLanguage !== action ) {
let currentLocale = Device . language . split ( '-' ) [ 0 ]
let locale = this . language . filter ( e => e . title === action ) [ 0 ] . locale
if ( currentLocale !== locale ) {
this . $showModal ( ConfirmDialog , {
props : {
title : "Restart required" ,
description : localize ( "EnRecipes needs to be restarted for the app language to take effect." ) ,
cancelButtonText : "CANCEL" ,
okButtonText : "RESTART" ,
} ,
} ) . then ( ( result ) => {
if ( result ) {
this . appLanguage = action
ApplicationSettings . setString ( "appLanguage" , action )
overrideLocale ( locale )
setTimeout ( ( e ) => utils . restartApp ( ) , 250 )
}
} )
}
}
} )
} ,
2020-11-10 18:28:48 +00:00
// THEME SELECTION
selectThemes ( ) {
2020-12-07 14:45:00 +00:00
this . $showModal ( ActionDialog , {
2020-10-21 17:54:45 +00:00
props : {
title : "Theme" ,
2020-12-07 14:45:00 +00:00
list : [ "Light" , "Dark" ] ,
2020-11-28 19:21:57 +00:00
height : "96" ,
2020-10-21 17:54:45 +00:00
} ,
2020-12-07 14:45:00 +00:00
} ) . then ( ( action ) => {
if ( action && action !== "Cancel" && this . appTheme !== action ) {
this . $showModal ( ConfirmDialog , {
2020-10-21 17:54:45 +00:00
props : {
2020-12-07 14:45:00 +00:00
title : "Restart required" ,
description : localize ( "EnRecipes needs to be restarted for the theme change to take effect." ) ,
2020-10-21 17:54:45 +00:00
cancelButtonText : "CANCEL" ,
2020-12-07 14:45:00 +00:00
okButtonText : "RESTART" ,
2020-10-21 17:54:45 +00:00
} ,
2020-12-07 14:45:00 +00:00
} ) . then ( ( result ) => {
if ( result ) {
2020-11-03 19:57:31 +00:00
this . appTheme = action
2020-12-07 14:45:00 +00:00
ApplicationSettings . setString ( "appTheme" , action )
setTimeout ( ( e ) => utils . restartApp ( ) , 250 )
2020-10-21 17:54:45 +00:00
}
2020-12-07 14:45:00 +00:00
} )
2020-10-21 17:54:45 +00:00
}
2020-12-07 14:45:00 +00:00
} )
2020-09-15 11:10:16 +00:00
} ,
2020-11-03 19:57:31 +00:00
2020-11-10 18:28:48 +00:00
// EXPORT HANDLERS
exportCheck ( ) {
2020-12-07 14:45:00 +00:00
if ( ! this . recipes . length ) {
2020-11-10 18:28:48 +00:00
Toast . makeText (
2020-12-07 14:45:00 +00:00
localize ( "Add at least one recipe to perform a backup" ) ,
2020-11-10 18:28:48 +00:00
"long"
) . show ( )
2020-12-07 14:45:00 +00:00
}
else {
2020-11-10 18:28:48 +00:00
this . permissionCheck (
this . permissionConfirmation ,
2020-12-07 14:45:00 +00:00
localize ( "EnRecipes requires storage permission in order to backup your data to this device." ) ,
2020-11-10 18:28:48 +00:00
this . exportBackup
2020-11-06 09:07:41 +00:00
)
}
2020-11-03 19:57:31 +00:00
} ,
2020-11-10 18:28:48 +00:00
exportBackup ( ) {
2020-12-07 14:45:00 +00:00
this . exportFiles ( "create" )
2020-11-03 19:57:31 +00:00
let date = new Date ( )
let formattedDate =
date . getFullYear ( ) +
"-" +
2020-12-07 14:45:00 +00:00
( "0" + ( date . getMonth ( ) + 1 ) ) . slice ( - 2 ) +
2020-11-03 19:57:31 +00:00
"-" +
2020-12-07 14:45:00 +00:00
( "0" + date . getDate ( ) ) . slice ( - 2 ) +
2020-11-03 19:57:31 +00:00
"_" +
2020-12-07 14:45:00 +00:00
( "0" + date . getHours ( ) ) . slice ( - 2 ) +
( "0" + date . getMinutes ( ) ) . slice ( - 2 ) +
( "0" + date . getSeconds ( ) ) . slice ( - 2 )
2020-11-15 21:13:06 +00:00
const sdDownloadPath = Folder . fromPath (
android . os . Environment . getExternalStorageDirectory ( ) . getAbsolutePath ( )
2020-12-07 14:45:00 +00:00
) . getFolder ( "Download" ) . path
let fromPath = path . join ( knownFolders . documents ( ) . path , "EnRecipes" )
2020-11-03 19:57:31 +00:00
let destPath = path . join (
sdDownloadPath ,
` EnRecipes-Backup_ ${ formattedDate } .zip `
)
2020-11-06 09:07:41 +00:00
this . backupInProgress = true
2020-12-07 14:45:00 +00:00
Zip . zip ( {
2020-11-03 19:57:31 +00:00
directory : fromPath ,
archive : destPath ,
2020-12-07 14:45:00 +00:00
onProgress : ( progress ) => {
2020-11-06 09:07:41 +00:00
this . backupProgress = progress
} ,
2020-12-07 14:45:00 +00:00
} ) . then ( ( success ) => {
2020-11-06 09:07:41 +00:00
Toast . makeText (
2020-11-15 21:13:06 +00:00
"Backup file successfully saved to Download folder" ,
2020-11-06 09:07:41 +00:00
"long"
) . show ( )
2020-12-07 14:45:00 +00:00
this . exportFiles ( "delete" )
} )
2020-09-15 11:10:16 +00:00
} ,
2020-12-07 14:45:00 +00:00
exportFiles ( option ) {
const folder = path . join ( knownFolders . documents ( ) . path , "EnRecipes" )
const EnRecipesFile = File . fromPath ( path . join ( folder , "recipes.json" ) )
2020-11-23 09:49:58 +00:00
let userCategoriesFile , userYieldUnitsFile , mealPlansFile
2020-12-07 14:45:00 +00:00
if ( this . userCategories . length ) {
2020-11-10 18:28:48 +00:00
userCategoriesFile = File . fromPath (
2020-12-07 14:45:00 +00:00
path . join ( folder , "userCategories.json" )
2020-11-10 18:28:48 +00:00
)
}
2020-12-07 14:45:00 +00:00
if ( this . userYieldUnits . length ) {
2020-11-10 18:28:48 +00:00
userYieldUnitsFile = File . fromPath (
2020-12-07 14:45:00 +00:00
path . join ( folder , "userYieldUnits.json" )
2020-11-10 18:28:48 +00:00
)
}
2020-12-07 14:45:00 +00:00
if ( this . mealPlans . length ) {
mealPlansFile = File . fromPath ( path . join ( folder , "mealPlans.json" ) )
2020-11-23 09:49:58 +00:00
}
2020-12-07 14:45:00 +00:00
switch ( option ) {
2020-11-10 18:28:48 +00:00
case "create" :
2020-12-07 14:45:00 +00:00
this . writeDataToFile ( EnRecipesFile , this . recipes )
2020-11-10 18:28:48 +00:00
this . userCategories . length &&
2020-12-07 14:45:00 +00:00
this . writeDataToFile ( userCategoriesFile , this . userCategories )
2020-11-10 18:28:48 +00:00
this . userYieldUnits . length &&
2020-12-07 14:45:00 +00:00
this . writeDataToFile ( userYieldUnitsFile , this . userYieldUnits )
2020-11-23 09:49:58 +00:00
this . mealPlans . length &&
2020-12-07 14:45:00 +00:00
this . writeDataToFile ( mealPlansFile , this . mealPlans )
2020-11-10 18:28:48 +00:00
break
case "delete" :
EnRecipesFile . remove ( )
this . userCategories . length && userCategoriesFile . remove ( )
this . userYieldUnits . length && userYieldUnitsFile . remove ( )
2020-11-23 09:49:58 +00:00
this . mealPlans . length && mealPlansFile . remove ( )
2020-11-10 18:28:48 +00:00
break
default :
break
2020-11-03 19:57:31 +00:00
}
} ,
2020-12-07 14:45:00 +00:00
writeDataToFile ( file , data ) {
file . writeText ( JSON . stringify ( data ) )
2020-11-03 19:57:31 +00:00
} ,
2020-11-10 18:28:48 +00:00
// IMPORT HANDLERS
importCheck ( ) {
2020-11-03 19:57:31 +00:00
this . permissionCheck (
2020-11-10 18:28:48 +00:00
this . permissionConfirmation ,
2020-12-07 14:45:00 +00:00
localize ( "EnRecipes requires storage permission in order to import your data from a previous backup." ) ,
2020-11-03 19:57:31 +00:00
this . openFilePicker
)
} ,
openFilePicker ( ) {
2020-12-07 14:45:00 +00:00
Filepicker . create ( {
mode : "single" ,
extensions : [ "zip" ] ,
} )
2020-11-10 18:28:48 +00:00
. present ( )
2020-12-07 14:45:00 +00:00
. then ( ( selection ) => {
Toast . makeText ( localize ( "Verifying..." ) ) . show ( )
let zipPath = selection [ 0 ]
this . validateZipContent ( zipPath )
} )
2020-11-06 09:07:41 +00:00
} ,
2020-12-07 14:45:00 +00:00
importDataToDB ( data , db , zipPath ) {
switch ( db ) {
2020-11-06 09:07:41 +00:00
case "EnRecipesDB" :
2020-12-07 14:45:00 +00:00
this . importImages ( zipPath )
this . importRecipesAction ( data )
2020-11-06 09:07:41 +00:00
break
case "userCategoriesDB" :
2020-12-07 14:45:00 +00:00
this . importCategoriesAction ( data )
2020-11-06 09:07:41 +00:00
break
case "userYieldUnitsDB" :
2020-12-07 14:45:00 +00:00
this . importYieldUnitsAction ( data )
2020-11-06 09:07:41 +00:00
break
2020-11-23 09:49:58 +00:00
case "mealPlansDB" :
2020-12-07 14:45:00 +00:00
this . importMealPlansAction ( data )
2020-11-23 09:49:58 +00:00
break
2020-11-06 09:07:41 +00:00
default :
break
}
} ,
2020-12-07 14:45:00 +00:00
isFileDataValid ( file ) {
file . forEach ( ( file , i ) => {
if ( File . exists ( file . path ) ) {
File . fromPath ( file . path )
2020-11-06 09:07:41 +00:00
. readText ( )
2020-12-07 14:45:00 +00:00
. then ( ( data ) => {
Array . isArray ( JSON . parse ( data ) ) &&
this . importDataToDB ( JSON . parse ( data ) , file . db , file . zipPath )
} )
2020-11-06 09:07:41 +00:00
}
2020-12-07 14:45:00 +00:00
} )
2020-11-03 19:57:31 +00:00
} ,
2020-12-07 14:45:00 +00:00
validateZipContent ( zipPath ) {
Zip . unzip ( {
2020-11-03 19:57:31 +00:00
archive : zipPath ,
overwrite : true ,
2020-12-07 14:45:00 +00:00
} ) . then ( ( extractedFolderPath ) => {
2020-11-06 09:07:41 +00:00
let cacheFolderPath = extractedFolderPath + "/EnRecipes"
2020-11-23 09:49:58 +00:00
const EnRecipesFilePath = cacheFolderPath + "/recipes.json"
2020-11-03 19:57:31 +00:00
const userCategoriesFilePath = cacheFolderPath + "/userCategories.json"
const userYieldUnitsFilePath = cacheFolderPath + "/userYieldUnits.json"
2020-11-23 09:49:58 +00:00
const mealPlansFilePath = cacheFolderPath + "/mealPlans.json"
2020-12-07 14:45:00 +00:00
if ( Folder . exists ( cacheFolderPath ) ) {
this . isFileDataValid ( [ {
2020-11-06 09:07:41 +00:00
zipPath ,
path : EnRecipesFilePath ,
db : "EnRecipesDB" ,
} ,
2020-12-07 14:45:00 +00:00
{
zipPath ,
path : userCategoriesFilePath ,
db : "userCategoriesDB"
} ,
{
zipPath ,
path : userYieldUnitsFilePath ,
db : "userYieldUnitsDB"
} ,
{
zipPath ,
path : mealPlansFilePath ,
db : "mealPlansDB"
} ,
] )
}
else {
Folder . fromPath ( extractedFolderPath ) . remove ( )
2020-11-06 09:07:41 +00:00
Toast . makeText (
2020-11-10 18:28:48 +00:00
"Import failed. Backup file is incorrect or corrupt" ,
2020-11-06 09:07:41 +00:00
"long"
) . show ( )
}
2020-12-07 14:45:00 +00:00
if ( Folder . exists ( cacheFolderPath + "/Images" ) ) {
this . importImages ( cacheFolderPath + "/Images" )
2020-11-03 19:57:31 +00:00
}
2020-12-07 14:45:00 +00:00
} )
2020-11-03 19:57:31 +00:00
} ,
2020-12-07 14:45:00 +00:00
importImages ( sourcePath ) {
2020-11-06 09:07:41 +00:00
let dest = knownFolders . documents ( ) . path
2020-12-07 14:45:00 +00:00
Zip . unzip ( {
2020-11-06 09:07:41 +00:00
archive : sourcePath ,
directory : dest ,
overwrite : true ,
2020-12-07 14:45:00 +00:00
} ) . then ( ( res ) => {
Toast . makeText ( localize ( "Import successful" ) , "long" ) . show ( )
2020-11-10 18:28:48 +00:00
this . $navigateBack ( )
2020-12-07 14:45:00 +00:00
} )
2020-11-03 19:57:31 +00:00
} ,
2020-11-10 18:28:48 +00:00
// PERMISSIONS HANDLER
2020-12-07 14:45:00 +00:00
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 )
else Toast . makeText ( localize ( "Permission denied" ) ) . show ( )
} )
2020-11-03 19:57:31 +00:00
}
2020-12-07 14:45:00 +00:00
} )
}
else {
Permissions . check ( "photo" ) . then ( ( res ) => {
let status = res [ Object . keys ( res ) [ 0 ] ]
if ( status !== "authorized" ) {
confirmation ( description ) . then ( ( e ) => {
2020-11-23 09:49:58 +00:00
e && utils . openAppSettingsPage ( )
2020-12-07 14:45:00 +00:00
} )
}
else action ( )
} )
2020-11-03 19:57:31 +00:00
}
} ,
2020-12-07 14:45:00 +00:00
permissionConfirmation ( description ) {
return this . $showModal ( ConfirmDialog , {
2020-11-10 18:28:48 +00:00
props : {
title : "Grant permission" ,
description ,
cancelButtonText : "NOT NOW" ,
okButtonText : "CONTINUE" ,
} ,
2020-12-07 14:45:00 +00:00
} )
2020-11-10 18:28:48 +00:00
} ,
2020-09-15 11:10:16 +00:00
} ,
2020-11-23 09:49:58 +00:00
mounted ( ) {
2020-12-07 14:45:00 +00:00
this . appTheme = ApplicationSettings . getString ( "appTheme" , "Light" )
this . appLanguage = ApplicationSettings . getString ( "appLanguage" , localize ( "System default" ) )
2020-10-14 19:32:32 +00:00
} ,
2020-09-15 11:10:16 +00:00
}
< / script >