restores images, data. updated logo

This commit is contained in:
Vishnu Raghav B 2020-11-06 14:37:41 +05:30
parent 43a1811f91
commit 3c0d077af1
66 changed files with 345 additions and 165 deletions

View file

@ -15,7 +15,7 @@
android {
defaultConfig {
versionCode 1
versionCode 2
versionName '1.0.0'
minSdkVersion 19
generatedDensities = []

View file

@ -5,7 +5,7 @@
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:name="com.tns.NativeScriptApplication" android:allowBackup="true" android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@style/AppTheme" android:requestLegacyExternalStorage="true">
<application android:name="com.tns.NativeScriptApplication" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:roundIcon="@drawable/ic_launcher_round" android:label="@string/app_name" android:hardwareAccelerated="true" android:largeHeap="true" android:theme="@style/AppTheme" android:requestLegacyExternalStorage="true">
<activity android:name="com.tns.NativeScriptActivity" android:label="@string/title_activity_kimera" android:screenOrientation="portrait" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|locale|uiMode" android:theme="@style/LaunchScreenTheme" android:windowSoftInputMode="adjustPan">
<meta-data android:name="SET_THEME_ON_LAUNCH" android:resource="@style/AppTheme" />
<intent-filter>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 780 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -2,10 +2,10 @@
<item>
<shape android:shape="rectangle">
<!-- <solid android:color="@android:color/white" /> -->
<solid android:color="#ff7043" />
<solid android:color="#ff5722" />
</shape>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/logo" />
<bitmap android:gravity="center" android:src="@drawable/logo_white" />
</item>
</layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -13,8 +13,7 @@ $grayL1: #e0e0e0;
$grayL2: #eeeeee;
$grayL3: #f5f5f5;
$grayL4: #fafafa;
$orange400: #ff7043;
$orange500: #ff5722;
$orange: #ff5722;
// Global SCSS styling
// @see https://docs.nativescript.org/ui/styling
@ -78,7 +77,7 @@ Page {
}
.fab-button {
color: white;
background-color: $orange500;
background-color: $orange;
}
.option,
.icon-option {
@ -97,13 +96,14 @@ Page {
}
.instruction {
border-color: $grayD4;
}
} // prettier-ignore
.text-btn,
.group-header,
.category,
ActivityIndicator,
.selected-sd-item {
color: $orange500;
.selected-sd-item,
Progress {
color: $orange;
}
}
.ns-dark {
@ -130,8 +130,7 @@ Page {
.recipeText,
.overviewItem,
.recipeItem,
.option-highlight,
.appIconContainer {
.option-highlight {
background: $grayD3;
}
.sd-item,
@ -144,13 +143,13 @@ Page {
}
.fab-button {
color: #111;
background: $orange400;
background: $orange;
}
.option,
.icon-option {
.bx,
.option-info {
color: $grayL2;
color: $gray;
}
}
.imageHolder {
@ -164,12 +163,14 @@ Page {
.instruction {
border-color: $grayL4;
}
// prettier-ignore
.text-btn,
.group-header,
.category,
ActivityIndicator,
.selected-sd-item {
color: $orange400;
.selected-sd-item,
Progress {
color: $orange;
}
}
// -----------------------------
@ -200,6 +201,10 @@ TabView {
margin-left: 8;
padding: 0 8;
}
// prettier-ignore
.progressContainer{
width: 100%;
}
// -----------------------------
// ActionBar
ActionBar {
@ -260,6 +265,10 @@ ActionBar {
color: $gray;
margin-bottom: 16;
}
.logo {
width: 64;
margin-bottom: 16;
}
.title {
font-size: 20;
text-align: center;
@ -280,7 +289,7 @@ RadListView {
}
.recipeItem {
margin: 8 16;
border-radius: 6;
border-radius: 4;
.recipeInfo {
margin: 4;
.category,
@ -304,10 +313,10 @@ RadListView {
.imageHolder {
vertical-alignment: center;
&.card {
border-radius: 6 0 0 6;
border-radius: 4 0 0 4;
// prettier-ignore
Image {
border-radius: 6 0 0 6;
border-radius: 4 0 0 4;
}
}
}
@ -316,7 +325,7 @@ RadListView {
background: #c62828;
color: #fff;
height: 128;
border-radius: 6;
border-radius: 4;
}
// -----------------------------
// SETTINGS
@ -333,22 +342,18 @@ RadListView {
}
.option-info {
font-size: 12;
line-height: 4;
}
}
}
// -----------------------------
// ABOUT
.appIconContainer {
padding: 32 0;
background: $orange;
padding: 16 0;
width: 100%;
.appIcon {
width: 56;
height: 56;
margin: 0 6 0 0;
padding: 0;
}
.appName {
font-size: 24;
height: 128;
}
}
// -----------------------------
@ -376,7 +381,7 @@ RadListView {
.overviewContainer {
margin-top: 12;
.overviewItem {
border-radius: 6;
border-radius: 4;
padding: 8;
margin: 8;
android-elevation: 1;
@ -405,7 +410,7 @@ RadListView {
padding-top: 4%;
margin: 0 0 0 8;
text-align: center;
border-radius: 100;
border-radius: 99;
&.square {
clip-path: polygon(
5% 0,
@ -437,7 +442,7 @@ RadListView {
.referenceItem {
padding: 14 16;
margin: 8 16 8;
border-radius: 6;
border-radius: 4;
font-size: 16;
.bx {
font-size: 24;
@ -452,7 +457,7 @@ RadListView {
line-height: 6;
padding: 16;
margin: 8 16 8;
border-radius: 6;
border-radius: 4;
}
}
// -----------------------------

View file

@ -19,12 +19,7 @@
orientation="horizontal"
class="appIconContainer"
>
<Image src="res://icon" class="appIcon" stretch="fill" />
<Label
text="EnRecipes"
verticalAlignment="center"
class="appName orkb"
/>
<Image src="res://logo_white" class="appIcon" stretch="aspectFit" />
</StackLayout>
<StackLayout orientation="horizontal" class="option">
<Label class="bx" :text="icon.info" />
@ -98,6 +93,7 @@ export default {
data() {
return {
viewIsScrolled: false,
appTheme: "Light",
}
},
methods: {

View file

@ -105,6 +105,7 @@
:showDrawer="showDrawer"
:hijackGlobalBackEvent="hijackGlobalBackEvent"
:releaseGlobalBackEvent="releaseGlobalBackEvent"
:openAppSettingsPage="openAppSettingsPage"
/>
</Frame>
</GridLayout>
@ -117,6 +118,7 @@ import {
Utils,
ApplicationSettings,
AndroidApplication,
Application,
} from "@nativescript/core"
import Theme from "@nativescript/theme"
@ -286,6 +288,7 @@ export default {
restartApp: this.restartApp,
hijackGlobalBackEvent: this.hijackGlobalBackEvent,
releaseGlobalBackEvent: this.releaseGlobalBackEvent,
openAppSettingsPage: this.openAppSettingsPage,
},
backstackVisible: false,
})
@ -326,6 +329,18 @@ export default {
)
android.os.Process.killProcess(android.os.Process.myPid())
},
openAppSettingsPage() {
const intent = new android.content.Intent(
android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
)
intent.addCategory(android.content.Intent.CATEGORY_DEFAULT)
intent.setData(
android.net.Uri.parse(
"package:" + Application.android.context.getPackageName()
)
)
Application.android.foregroundActivity.startActivity(intent)
},
showDrawer() {
this.$refs.drawer.nativeView.showDrawer()
},

View file

@ -28,7 +28,6 @@
scrollBarIndicatorVisible="false"
>
<StackLayout width="100%" padding="0 0 128">
<!-- Image and camera btn -->
<AbsoluteLayout>
<StackLayout
width="100%"
@ -65,7 +64,6 @@
</StackLayout>
</AbsoluteLayout>
<!-- Primary information -->
<StackLayout margin="0 16">
<AbsoluteLayout class="inputField">
<TextField
@ -115,7 +113,6 @@
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- Ingredients section -->
<StackLayout margin="0 16">
<Label text="Ingredients" class="sectionTitle" />
<GridLayout
@ -161,7 +158,6 @@
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- Instructions section -->
<StackLayout margin="0 16">
<Label text="Instructions" class="sectionTitle" />
<GridLayout
@ -191,7 +187,6 @@
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- Notes section -->
<StackLayout margin="0 16">
<Label text="Notes" class="sectionTitle" />
<GridLayout
@ -217,7 +212,6 @@
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- References section -->
<StackLayout margin="0 16">
<Label text="References" class="sectionTitle" />
<GridLayout
@ -265,6 +259,7 @@ import {
knownFolders,
Utils,
File,
ApplicationSettings,
} from "@nativescript/core"
import { Mediafilepicker } from "nativescript-mediafilepicker"
@ -276,10 +271,11 @@ import ActionDialog from "./modal/ActionDialog.vue"
import ConfirmDialog from "./modal/ConfirmDialog.vue"
import PromptDialog from "./modal/PromptDialog.vue"
import ListPicker from "./modal/ListPicker.vue"
import { create } from "domain"
import * as Permissions from "@nativescript-community/perms"
import * as Toast from "nativescript-toast"
export default {
props: ["recipeIndex", "recipeID", "selectedCategory"],
props: ["recipeIndex", "recipeID", "selectedCategory", "openAppSettingsPage"],
data() {
return {
title: "New recipe",
@ -519,14 +515,64 @@ export default {
}).then((action) => {
this.blockModal = false
if (action) {
this.imagePicker()
this.permissionCheck(
this.imagePickerPermissionConfirmation,
this.imagePicker
)
} else if (action != null) {
this.recipeContent.imageSrc = null
this.releaseBackEvent()
}
})
} else {
this.imagePicker()
this.permissionCheck(
this.imagePickerPermissionConfirmation,
this.imagePicker
)
}
},
imagePickerPermissionConfirmation() {
return this.$showModal(ConfirmDialog, {
props: {
title: "Grant permission",
description:
"EnRecipes requires storage and camera permission in order to set recipe photo.",
cancelButtonText: "NOT NOW",
okButtonText: "CONTINUE",
},
})
},
permissionCheck(confirmation, action) {
if (!ApplicationSettings.getBoolean("storagePermissionAsked", false)) {
confirmation().then((e) => {
if (e) {
Permissions.request("camera").then((res) => {
let status = res[Object.keys(res)[0]]
if (status === "authorized") action()
if (status === "never_ask_again")
ApplicationSettings.setBoolean("storagePermissionAsked", true)
if (status === "denied")
Toast.makeText("Permission denied").show()
})
}
})
} else {
Permissions.check("camera").then((res) => {
if (res[0] !== "authorized") {
confirmation().then((e) => {
e && this.openAppSettingsPage()
})
} else {
Permissions.request("storage").then((res) => {
let status = res[Object.keys(res)[0]]
if (status !== "authorized") {
confirmation().then((e) => {
e && this.openAppSettingsPage()
})
} else action()
})
}
})
}
},
imagePicker() {
@ -541,6 +587,7 @@ export default {
},
})
mediafilepicker.on("getFiles", (image) => {
ApplicationSettings.setBoolean("storagePermissionAsked", true)
vm.recipeContent.imageSrc = image.object.get("results")[0].file
})
},
@ -611,7 +658,10 @@ export default {
},
imageSaveOperation() {
let imgSavedToPath = path.join(
knownFolders.documents().getFolder("EnRecipes").path,
knownFolders
.documents()
.getFolder("EnRecipes")
.getFolder("Images").path,
`${this.getRandomID()}.jpg`
)
let workerService = new WorkerService()

View file

@ -76,6 +76,8 @@
v-if="recipe.imageSrc"
:src="recipe.imageSrc"
stretch="aspectFill"
decodeWidth="112"
decodeHeight="112"
/>
<Label
row="0"
@ -118,7 +120,7 @@
class="noResult"
v-if="!recipes.length && !filterFavorites && !filterTrylater"
>
<Label class="bx icon" :text="icon.plusCircle" textWrap="true" />
<Image class="logo" src="res://icon_gray" stretch="aspectFit" />
<Label
class="title orkm"
text="Start adding your recipes!"
@ -199,10 +201,7 @@
</template>
<script>
import {
Utils,
AndroidApplication,
} from "@nativescript/core"
import { Utils, AndroidApplication } from "@nativescript/core"
import EditRecipe from "./EditRecipe.vue"
import ViewRecipe from "./ViewRecipe.vue"
@ -218,6 +217,7 @@ export default {
"showDrawer",
"hijackGlobalBackEvent",
"releaseGlobalBackEvent",
"openAppSettingsPage",
],
components: {
EditRecipe,
@ -434,6 +434,7 @@ export default {
// },
props: {
selectedCategory: this.selectedCategory,
openAppSettingsPage: this.openAppSettingsPage,
},
})
},

View file

@ -22,23 +22,46 @@
>
<Label verticalAlignment="center" class="bx" :text="icon.theme" />
<StackLayout>
<Label text="Theme" class="option-title" />
<Label text="Theme" />
<Label :text="appTheme" class="option-info" textWrap="true" />
</StackLayout>
</StackLayout>
<StackLayout class="hr m-10"></StackLayout>
<Label text="Backup/Restore" class="group-header" />
<Label text="Database" class="group-header" />
<StackLayout orientation="horizontal" class="option" @tap="backupCheck">
<Label class="bx" :text="icon.save" />
<Label text="Backup data" />
<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>
</StackLayout>
<StackLayout
orientation="horizontal"
class="option"
@tap="restoreCheck"
>
<Label class="bx" :text="icon.restore" />
<Label text="Restore data" />
<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>
</StackLayout>
</StackLayout>
</ScrollView>
@ -64,7 +87,6 @@ import ActionDialog from "./modal/ActionDialog.vue"
import ConfirmDialog from "./modal/ConfirmDialog.vue"
import { Couchbase } from "nativescript-couchbase-plugin"
const recipesDB = new Couchbase("EnRecipes")
import { mapState, mapActions } from "vuex"
export default {
props: [
@ -73,11 +95,14 @@ export default {
"restartApp",
"hijackGlobalBackEvent",
"releaseGlobalBackEvent",
"openAppSettingsPage",
],
data() {
return {
viewIsScrolled: false,
appTheme: "Light",
backupProgress: 0,
backupInProgress: false,
}
},
computed: {
@ -90,7 +115,12 @@ export default {
]),
},
methods: {
...mapActions(["setCurrentComponentAction"]),
...mapActions([
"setCurrentComponentAction",
"importCategoriesAction",
"importYieldUnitsAction",
"importRecipesAction",
]),
initializePage() {
this.setCurrentComponentAction("Settings")
this.releaseGlobalBackEvent()
@ -112,9 +142,9 @@ export default {
if (action && action !== "Cancel" && this.appTheme !== action) {
this.$showModal(ConfirmDialog, {
props: {
title: "App Reload Required",
title: "Reload required",
description:
"The app needs to be reloaded for the theme change to take effect.",
"EnRecipes needs to be reloaded for the theme change to take effect.",
cancelButtonText: "CANCEL",
okButtonText: "RELOAD",
},
@ -130,26 +160,22 @@ export default {
},
writeFile(file, data) {
file
.writeText(JSON.stringify(data))
.then((res) => {
file.readText().then((res) => {
// console.log("Data: ", res)
})
})
.catch((err) => {
console.log(err)
})
file.writeText(JSON.stringify(data))
},
BackupDataFiles(option) {
const folder = path.join(knownFolders.documents().path, "EnRecipes")
const EnRecipesFile = File.fromPath(path.join(folder, "EnRecipes.json"))
const userCategoriesFile = File.fromPath(
path.join(folder, "userCategories.json")
)
const userYieldUnitsFile = File.fromPath(
path.join(folder, "userYieldUnits.json")
)
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")
)
}
switch (option) {
case "create":
this.writeFile(EnRecipesFile, this.recipes)
@ -199,28 +225,34 @@ export default {
sdDownloadPath,
`EnRecipes-Backup_${formattedDate}.zip`
)
this.backupInProgress = true
Zip.zip({
directory: fromPath,
archive: destPath,
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")
})
.then((success) => {
Toast.makeText(
"Backup file successfully saved to Downloads",
"long"
).show()
console.log("success:" + success)
this.BackupDataFiles("delete")
})
.catch((err) => {
console.log(err)
})
},
backupCheck(args) {
let btn = args.object
this.highlight(args)
if (!this.recipes.length) {
Toast.makeText("To perform a backup, add at least one recipe").show()
Toast.makeText(
"Add at least one recipe to perform a backup",
"long"
).show()
} else {
this.permissionCheck(this.backupPermissionConfirmation, this.backupData)
}
@ -237,7 +269,6 @@ export default {
},
})
},
restoreData() {},
restoreCheck(args) {
let btn = args.object
this.highlight(args)
@ -258,58 +289,77 @@ export default {
let zipPath = result
let dest = knownFolders.documents().path
this.validateZipContent(zipPath)
// Zip.unzip({
// archive: zipPath,
// directory: dest,
// overwrite: true,
// })
// .then((success) => {
// this.restoreDataInDB()
// Toast.makeText("Restore successful!").show()
// })
// .catch((err) => {
// console.log(err)
// })
})
},
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)
})
}
})
},
validateZipContent(zipPath) {
Zip.unzip({
archive: zipPath,
overwrite: true,
}).then((success) => {
let cacheFolderPath = success + "/EnRecipes"
}).then((extractedFolderPath) => {
let cacheFolderPath = extractedFolderPath + "/EnRecipes"
const EnRecipesFilePath = cacheFolderPath + "/EnRecipes.json"
const userCategoriesFilePath = cacheFolderPath + "/userCategories.json"
const userYieldUnitsFilePath = cacheFolderPath + "/userYieldUnits.json"
if (
Folder.exists(cacheFolderPath) &&
File.exists(EnRecipesFilePath) &&
File.exists(userCategoriesFilePath) &&
File.exists(userCategoriesFilePath)
) {
console.log("Zip intact")
// Check if EnRecipes.json is of type array
File.fromPath(EnRecipesFilePath)
.readText()
.then((data) => {
let EnRecipesData = JSON.parse(data)
console.log(Array.isArray(EnRecipesData))
EnRecipesData.forEach(recipe => {
})
console.log(EnRecipesData)
})
if (Folder.exists(cacheFolderPath)) {
this.isImportedDataValid([
{
zipPath,
path: EnRecipesFilePath,
db: "EnRecipesDB",
},
{ zipPath, path: userCategoriesFilePath, db: "userCategoriesDB" },
{ zipPath, path: userYieldUnitsFilePath, db: "userYieldUnitsDB" },
])
} else {
Folder.fromPath(success).remove()
console.log("Zip modified externally or incorrect file")
Folder.fromPath(extractedFolderPath).remove()
Toast.makeText(
"Zip modified externally or incorrect file",
"long"
).show()
}
if (Folder.exists(cacheFolderPath + "/Images")) {
this.copyImages(cacheFolderPath + "/Images")
}
})
},
restoreDataInDB() {
// recipesDB.android
// recipesDB.android.destroyDatabase()
copyImages(sourcePath) {
let dest = knownFolders.documents().path
Zip.unzip({
archive: sourcePath,
directory: dest,
overwrite: true,
}).then((res) => {
Toast.makeText("Import successful!", "long").show()
})
},
permissionCheck(confirmation, action) {
if (!ApplicationSettings.getBoolean("storagePermissionAsked", false)) {
confirmation().then((e) => {
@ -334,18 +384,6 @@ export default {
})
}
},
openAppSettingsPage() {
const intent = new android.content.Intent(
android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
)
intent.addCategory(android.content.Intent.CATEGORY_DEFAULT)
intent.setData(
android.net.Uri.parse(
"package:" + Application.android.context.getPackageName()
)
)
Application.android.foregroundActivity.startActivity(intent)
},
},
created() {
this.appTheme = ApplicationSettings.getString("appTheme", "Light")

View file

@ -474,7 +474,6 @@ export default {
: 1
},
isLightMode() {
console.log(Application.systemAppearance())
return Application.systemAppearance() === "light"
},
},
@ -555,9 +554,7 @@ export default {
shareRecipe() {
let overview = `${
this.recipe.title
}\n\nTime required: ${this.formattedTime(
this.recipe.timeRequired
)}\n`
}\n\nTime required: ${this.formattedTime(this.recipe.timeRequired)}\n`
let shareContent = overview
if (this.recipe.ingredients.length) {
let ingredients = `\n\nIngredients for ${

View file

@ -2,7 +2,7 @@ import Vue from "vue"
import Vuex from "vuex"
import { Couchbase } from "nativescript-couchbase-plugin"
import { getFileAccess } from "@nativescript/core"
const recipesDB = new Couchbase("EnRecipes")
const EnRecipesDB = new Couchbase("EnRecipes")
const userCategoriesDB = new Couchbase("userCategories")
const userYieldUnitsDB = new Couchbase("userYieldUnits")
@ -163,7 +163,6 @@ export default new Vuex.Store({
share: "\uedf3",
edit: "\uedba",
theme: "\uecaa",
restore: "\uea72",
link: "\ueaa0",
file: "\ued02",
user: "\uee33",
@ -179,12 +178,14 @@ export default new Vuex.Store({
item: "\ue99d",
step: "\ue948",
source: "\ueaa0",
export: "\ued07",
import: "\ued0c",
},
currentComponent: "EnRecipes",
},
mutations: {
initializeRecipes(state) {
let a = recipesDB.query({ select: [] })
let a = EnRecipesDB.query({ select: [] })
a.forEach((e) => {
state.recipes.push(e)
})
@ -228,7 +229,45 @@ export default new Vuex.Store({
},
addRecipe(state, { id, recipe }) {
state.recipes.push(recipe)
recipesDB.createDocument(recipe, id)
EnRecipesDB.createDocument(recipe, id)
},
importRecipes(state, recipes) {
console.log("hello")
let localRecipesIDs, partition
if (state.recipes.length) {
localRecipesIDs = state.recipes.map((e) => e.id)
partition = recipes.reduce(
(result, recipe, i) => {
localRecipesIDs.indexOf(recipe.id) < 0
? result[0].push(recipe) // create candidates
: result[1].push(recipe) // update candidates
return result
},
[[], []]
)
if (partition[0].length) createDocuments(partition[0])
if (partition[1].length) updateDocuments(partition[1])
} else {
createDocuments(recipes)
}
function createDocuments(data) {
console.log("creating")
state.recipes = [...state.recipes, ...data]
data.forEach((recipe) => {
EnRecipesDB.createDocument(recipe, recipe.id)
})
}
function updateDocuments(data) {
console.log("updating")
data.forEach((recipe) => {
let recipeIndex = state.recipes
.map((e, i) => (e.id === recipe.id ? i : -1))
.filter((e) => e >= 0)[0]
console.log(recipeIndex)
Object.assign(state.recipes[recipeIndex], recipe)
EnRecipesDB.updateDocument(recipe.id, recipe)
})
}
},
addCategory(state, category) {
let lowercase = state.categories.map((e) => e.toLowerCase())
@ -241,33 +280,48 @@ export default new Vuex.Store({
state.categories.sort()
}
},
addYieldUnit(state, unit) {
importCategories(state, categories) {
state.userCategories = new Set([...state.userCategories, ...categories])
userCategoriesDB.updateDocument("userCategories", {
userCategories: [...state.userCategories],
})
state.categories = [...defaultCategories, ...state.userCategories]
state.categories.sort()
},
addYieldUnit(state, yieldUnit) {
let lowercase = state.yieldUnits.map((e) => e.toLowerCase())
if (lowercase.indexOf(unit.toLowerCase()) == -1) {
state.userYieldUnits.push(unit)
if (lowercase.indexOf(yieldUnit.toLowerCase()) == -1) {
state.userYieldUnits.push(yieldUnit)
userYieldUnitsDB.updateDocument("userYieldUnits", {
userYieldUnits: [...state.userYieldUnits],
})
state.yieldUnits = [...defaultYieldUnits, ...state.userYieldUnits]
}
},
importYieldUnits(state, yieldUnits) {
state.userYieldUnits = new Set([...state.userYieldUnits, ...yieldUnits])
userYieldUnitsDB.updateDocument("userYieldUnits", {
userYieldUnits: [...state.userYieldUnits],
})
state.yieldUnits = [...defaultYieldUnits, ...state.userYieldUnits]
},
overwriteRecipe(state, { index, id, recipe }) {
Object.assign(state.recipes[index], recipe)
recipesDB.updateDocument(id, recipe)
EnRecipesDB.updateDocument(id, recipe)
},
deleteRecipe(state, { index, id }) {
getFileAccess().deleteFile(state.recipes[index].imageSrc)
state.recipes.splice(index, 1)
recipesDB.deleteDocument(id)
EnRecipesDB.deleteDocument(id)
},
toggleState(state, { index, id, recipe, key, setDate }) {
state.recipes[index][key] = !state.recipes[index][key]
if (setDate) state.recipes[index].lastTried = new Date()
recipesDB.updateDocument(id, recipe)
EnRecipesDB.updateDocument(id, recipe)
},
setLastTriedDate(state, index) {
state.recipes[index].lastTried = new Date()
recipesDB.updateDocument(state.recipes[index].id, state.recipes[index])
EnRecipesDB.updateDocument(state.recipes[index].id, state.recipes[index])
},
setCurrentComponent(state, comp) {
state.currentComponent = comp
@ -285,8 +339,8 @@ export default new Vuex.Store({
state.recipes.forEach((e, i) => {
if (e.category == current) {
state.recipes[i].category = updated
recipesDB.inBatch(() => {
recipesDB.updateDocument(state.recipes[i].id, state.recipes[i])
EnRecipesDB.inBatch(() => {
EnRecipesDB.updateDocument(state.recipes[i].id, state.recipes[i])
})
}
})
@ -305,12 +359,21 @@ export default new Vuex.Store({
addRecipeAction({ commit }, recipe) {
commit("addRecipe", recipe)
},
importRecipesAction({ commit }, recipes) {
commit("importRecipes", recipes)
},
addCategoryAction({ commit }, category) {
commit("addCategory", category)
},
importCategoriesAction({ commit }, categories) {
commit("importCategories", categories)
},
addYieldUnitAction({ commit }, yieldUnit) {
commit("addYieldUnit", yieldUnit)
},
importYieldUnitsAction({ commit }, yieldUnits) {
commit("importYieldUnits", yieldUnits)
},
overwriteRecipeAction({ commit }, updatedRecipe) {
commit("overwriteRecipe", updatedRecipe)
},

View file

@ -1,11 +1,16 @@
require("tns-core-modules/globals")
import { ImageSource } from "@nativescript/core"
import { ImageSource, ImageAsset } from "@nativescript/core"
global.onmessage = function({ data }) {
let imgFile = data.imgFile
let imgSavedToPath = data.imgSavedToPath
ImageSource.fromFile(imgFile).then((imgData) => {
if (imgData.saveToFile(imgSavedToPath, "jpg")) {
let imgAsset = new ImageAsset(data.imgFile)
imgAsset.options = {
width: 1200,
height: 1200,
keepAspectRatio: true,
}
ImageSource.fromAsset(imgAsset).then((imgData) => {
if (imgData.saveToFile(imgSavedToPath, "jpg", 75)) {
global.postMessage("savedToFile")
}
})