enrecipes/app/components/EditRecipe.vue

641 lines
18 KiB
Vue
Raw Normal View History

2020-10-14 19:32:32 +00:00
<template>
2020-10-26 20:49:54 +00:00
<Page @unloaded="releaseBackEvent">
2020-10-14 19:32:32 +00:00
<ActionBar :flat="viewIsScrolled ? false : true">
<GridLayout rows="*" columns="auto, *, auto," class="actionBarContainer">
<Label
class="bx leftAction"
:text="icon.back"
automationText="Back"
col="0"
@tap="navigateBack"
/>
2020-10-21 17:54:45 +00:00
<Label class="title orkm" :text="title" col="1" />
2020-10-14 19:32:32 +00:00
<Label
v-if="hasEnoughDetails"
class="bx"
:text="icon.save"
col="2"
@tap="saveRecipe"
/>
</GridLayout>
</ActionBar>
<ScrollView
width="100%"
height="100%"
@scroll="onScroll($event)"
scrollBarIndicatorVisible="false"
>
<StackLayout width="100%">
<!-- Image and camera btn -->
<AbsoluteLayout>
<StackLayout
width="100%"
:height="screenWidth"
class="view-imageHolder"
verticalAlignment="center"
>
<Image
v-if="recipeContent.imageSrc"
:src="recipeContent.imageSrc"
stretch="aspectFill"
width="100%"
:height="screenWidth"
/>
<Label
v-else
horizontalAlignment="center"
class="bx"
fontSize="160"
2020-10-21 17:54:45 +00:00
:text="icon.image"
2020-10-14 19:32:32 +00:00
/>
</StackLayout>
<StackLayout
width="100%"
horizontalAlignment="center"
:top="screenWidth - 42"
>
<Label
v-if="recipeContent.imageSrc"
2020-10-22 18:36:50 +00:00
@tap="removePicture"
class="bx fab-button"
2020-10-14 19:32:32 +00:00
:text="icon.close"
androidElevation="8"
/>
2020-10-26 20:49:54 +00:00
<GridLayout v-else rows="auto" columns="*, auto, auto, *">
<Label
col="1"
@tap="takePicture"
class="bx fab-button"
:text="icon.camera"
androidElevation="8"
/>
<Label
col="2"
@tap="selectPicture"
class="bx fab-button"
:text="icon.image"
androidElevation="8"
/>
</GridLayout>
2020-10-14 19:32:32 +00:00
</StackLayout>
</AbsoluteLayout>
<!-- Primary information -->
<StackLayout margin="0 16">
<AbsoluteLayout class="inputField">
<TextField
hint="My Healthy Recipe"
v-model="recipeContent.title"
autocapitalizationType="words"
/>
<Label top="0" class="fieldLabel" text="Title" />
</AbsoluteLayout>
<AbsoluteLayout class="inputField">
<TextField
v-model="recipeContent.category"
editable="false"
@tap="showCategories()"
/>
<Label top="0" class="fieldLabel" text="Category" />
</AbsoluteLayout>
<GridLayout columns="*, 8, *">
<AbsoluteLayout class="inputField" col="0">
<TimePickerField
2020-10-22 18:36:50 +00:00
titleTextColor="red"
2020-10-14 19:32:32 +00:00
timeFormat="HH:mm"
pickerTitle="Approx. preparation time"
@timeChange="onPrepTimeChange"
:time="recipeContent.prepTime"
></TimePickerField>
<Label top="0" class="fieldLabel" text="Preparation time" />
</AbsoluteLayout>
<AbsoluteLayout class="inputField" col="2">
<TimePickerField
timeFormat="HH:mm"
pickerTitle="Approx. cooking time"
@timeChange="onCookTimeChange"
:time="recipeContent.cookTime"
></TimePickerField>
<Label top="0" class="fieldLabel" text="Cooking time" />
</AbsoluteLayout>
</GridLayout>
<GridLayout columns="*, 16, *">
<AbsoluteLayout class="inputField" col="0">
<TextField
width="100%"
keyboardType="number"
v-model="recipeContent.portionSize"
/>
<Label top="0" class="fieldLabel" text="Portion size" />
</AbsoluteLayout>
</GridLayout>
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- Ingredients section -->
<StackLayout margin="0 16">
<Label text="Ingredients" class="sectionTitle" />
<GridLayout
columns="*,8,auto,8,auto,8,auto"
v-for="(ingredient, index) in recipeContent.ingredients"
:key="index"
>
<TextField
col="0"
v-model="recipeContent.ingredients[index].item"
:hint="`Item ${index + 1}`"
autocapitalizationType="words"
/>
<TextField
width="72"
col="2"
v-model="recipeContent.ingredients[index].quantity"
hint="1.000"
keyboardType="number"
/>
<TextField
width="64"
col="4"
v-model="recipeContent.ingredients[index].unit"
hint="Unit"
editable="false"
@tap="showUnits($event)"
/>
<Label
col="6"
2020-10-21 17:54:45 +00:00
class="bx closeBtn"
2020-10-14 19:32:32 +00:00
:text="icon.close"
@tap="removeIngredient(index)"
/>
</GridLayout>
<Label
class="sec-btn pull-left orkm"
text="+ ADD INGREDIENT"
@tap="addIngredient()"
/>
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- Instructions section -->
<StackLayout margin="0 16">
<Label text="Instructions" class="sectionTitle" />
<GridLayout
columns="*,8,auto"
v-for="(instruction, index) in recipeContent.instructions"
:key="index"
>
<TextView
col="0"
:hint="`Step ${index + 1}`"
v-model="recipeContent.instructions[index]"
editable="true"
/>
<Label
col="2"
2020-10-21 17:54:45 +00:00
class="bx closeBtn"
2020-10-14 19:32:32 +00:00
:text="icon.close"
@tap="removeInstruction(index)"
/>
</GridLayout>
<Label
class="sec-btn pull-left orkm"
text="+ ADD STEP"
@tap="addInstruction()"
/>
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- Notes section -->
<StackLayout margin="0 16">
<Label text="Notes" class="sectionTitle" />
<GridLayout
columns="*,8,auto"
v-for="(note, index) in recipeContent.notes"
:key="index"
>
<TextView
col="0"
v-model="recipeContent.notes[index]"
:hint="`Note ${index + 1}`"
editable="true"
/>
<Label
col="2"
2020-10-21 17:54:45 +00:00
class="bx closeBtn"
2020-10-14 19:32:32 +00:00
:text="icon.close"
@tap="removeNote(index)"
/>
</GridLayout>
<Label
class="sec-btn pull-left orkm"
text="+ ADD NOTE"
@tap="addNote()"
/>
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- References section -->
<StackLayout margin="0 16">
<Label text="References" class="sectionTitle" />
<GridLayout
columns="*,8,auto"
v-for="(reference, index) in recipeContent.references"
:key="index"
>
<TextField
col="0"
v-model="recipeContent.references[index]"
hint="Website or Video URL"
/>
<Label
col="2"
2020-10-21 17:54:45 +00:00
class="bx closeBtn"
2020-10-14 19:32:32 +00:00
:text="icon.close"
@tap="removeReference(index)"
/>
</GridLayout>
<Label
class="sec-btn pull-left orkm"
text="+ ADD REFERENCE"
@tap="addReference()"
/>
<StackLayout margin="32"></StackLayout>
</StackLayout>
</StackLayout>
</ScrollView>
</Page>
</template>
<script>
import {
Screen,
AndroidApplication,
ImageSource,
path,
getFileAccess,
knownFolders,
} from "@nativescript/core"
2020-10-26 20:49:54 +00:00
import * as imagepicker from "nativescript-imagepicker"
import * as camera from "@nativescript/camera"
2020-10-22 18:36:50 +00:00
2020-10-14 19:32:32 +00:00
import { mapState, mapActions } from "vuex"
2020-10-22 18:36:50 +00:00
2020-10-21 17:54:45 +00:00
import ActionDialog from "./modal/ActionDialog.vue"
import PromptDialog from "./modal/PromptDialog.vue"
import ConfirmDialog from "./modal/ConfirmDialog.vue"
2020-10-14 19:32:32 +00:00
export default {
2020-10-26 20:49:54 +00:00
props: ["recipeIndex", "recipeID", "selectedCategory"],
2020-10-14 19:32:32 +00:00
data() {
return {
2020-10-21 17:54:45 +00:00
title: "New recipe",
2020-10-14 19:32:32 +00:00
viewIsScrolled: false,
recipeContent: {
imageSrc: null,
title: null,
category: null,
prepTime: "00:00",
cookTime: "00:00",
portionSize: 1,
ingredients: [
{
2020-10-21 17:54:45 +00:00
item: "",
2020-10-14 19:32:32 +00:00
quantity: null,
unit: "unit",
},
],
instructions: [""],
notes: [""],
references: [""],
isFavorite: false,
2020-10-21 17:54:45 +00:00
tried: false,
lastModified: null,
2020-10-14 19:32:32 +00:00
},
2020-10-26 20:49:54 +00:00
tempRecipeContent: {
imageSrc: null,
title: null,
category: null,
prepTime: "00:00",
cookTime: "00:00",
portionSize: 1,
ingredients: [
{
item: "",
quantity: null,
unit: "unit",
},
],
instructions: [""],
notes: [""],
references: [""],
isFavorite: false,
tried: false,
lastModified: null,
},
2020-10-21 17:54:45 +00:00
blockModal: false,
newRecipeID: null,
2020-10-14 19:32:32 +00:00
}
},
computed: {
2020-10-26 20:49:54 +00:00
...mapState(["icon", "units", "recipes", "categories", "currentComponent"]),
2020-10-14 19:32:32 +00:00
screenWidth() {
2020-10-22 18:36:50 +00:00
return Screen.mainScreen.widthDIPs
2020-10-14 19:32:32 +00:00
},
hasEnoughDetails() {
if (this.recipeID) {
2020-10-21 17:54:45 +00:00
return (
JSON.stringify(this.recipeContent) !==
JSON.stringify(this.tempRecipeContent)
)
} else {
return this.recipeContent.title
}
2020-10-14 19:32:32 +00:00
},
},
methods: {
2020-10-26 20:49:54 +00:00
...mapActions([
"setCurrentComponentAction",
"addRecipeAction",
"overwriteRecipeAction",
"addCategoryAction",
]),
getRandomID() {
let res = ""
let chars = "abcdefghijklmnopqrstuvwxyz0123456789"
for (let i = 0; i < 10; i++) {
res += chars.charAt(Math.floor(Math.random() * chars.length))
}
return res
2020-10-21 17:54:45 +00:00
},
setTime(key, time) {
if (Date.parse(time)) {
let date = new Date(time)
2020-10-14 19:32:32 +00:00
let h = date.getHours()
let m = date.getMinutes()
2020-10-21 17:54:45 +00:00
this.recipeContent[key] =
2020-10-14 19:32:32 +00:00
(h < 10 ? "0" + h : h) + ":" + (m < 10 ? "0" + m : m)
}
2020-10-21 17:54:45 +00:00
},
clearEmptyFields() {
if (!this.recipeContent.portionSize) {
this.recipeContent.portionSize = 1
}
if (!this.recipeContent.category) {
this.recipeContent.category = "Undefined"
}
this.recipeContent.ingredients.forEach((e, i) => {
if (!e.item.length) {
this.recipeContent.ingredients.splice(i, 1)
}
})
let vm = this
function removeEmpty(arr) {
vm.recipeContent[arr].forEach((e, i) => {
if (!e.length) {
vm.recipeContent[arr].splice(i, 1)
}
})
}
removeEmpty("instructions")
removeEmpty("notes")
removeEmpty("references")
2020-10-14 19:32:32 +00:00
},
saveRecipe() {
2020-10-21 17:54:45 +00:00
this.clearEmptyFields()
this.recipeContent.lastModified = new Date()
if (this.recipeID) {
2020-10-26 20:49:54 +00:00
this.overwriteRecipeAction({
index: this.recipeIndex,
id: this.recipeID,
recipe: this.recipeContent,
})
2020-10-21 17:54:45 +00:00
} else {
this.recipeContent.id = this.newRecipeID
2020-10-26 20:49:54 +00:00
this.addRecipeAction({
id: this.newRecipeID,
recipe: this.recipeContent,
})
}
if (this.tempRecipeContent.imageSrc && !this.recipeContent.imageSrc) {
getFileAccess().deleteFile(this.tempRecipeContent.imageSrc)
2020-10-21 17:54:45 +00:00
}
2020-10-14 19:32:32 +00:00
this.$navigateBack()
},
onPrepTimeChange(args) {
2020-10-21 17:54:45 +00:00
this.setTime("prepTime", args.value)
2020-10-14 19:32:32 +00:00
},
onCookTimeChange(args) {
2020-10-21 17:54:45 +00:00
this.setTime("cookTime", args.value)
2020-10-14 19:32:32 +00:00
},
onScroll(args) {
args.scrollY
? (this.viewIsScrolled = true)
: (this.viewIsScrolled = false)
},
showCategories() {
2020-10-21 17:54:45 +00:00
this.releaseBackEvent()
this.$showModal(ActionDialog, {
props: {
title: "Category",
list: [...this.categories],
2020-10-22 18:36:50 +00:00
height: "60%",
2020-10-21 17:54:45 +00:00
action: "NEW CATEGORY",
},
}).then((action) => {
if (action == "NEW CATEGORY") {
this.$showModal(PromptDialog, {
props: {
title: "New category",
action: "ADD",
},
2020-10-26 20:49:54 +00:00
}).then((category) => {
2020-10-21 17:54:45 +00:00
this.hijackBackEvent()
2020-10-26 20:49:54 +00:00
if (category.length) {
this.recipeContent.category = category
this.addCategoryAction(category)
2020-10-21 17:54:45 +00:00
}
})
} else if (action) {
this.recipeContent.category = action
this.hijackBackEvent()
} else {
this.hijackBackEvent()
2020-10-14 19:32:32 +00:00
}
2020-10-21 17:54:45 +00:00
})
2020-10-14 19:32:32 +00:00
},
navigateBack() {
2020-10-21 17:54:45 +00:00
if (this.hasEnoughDetails) {
this.blockModal = true
this.$showModal(ConfirmDialog, {
props: {
title: "Discard changes",
description:
"Are you sure you want discard unsaved changes to this recipe?",
cancelButtonText: "KEEP EDITING",
okButtonText: "DISCARD",
},
}).then((action) => {
this.blockModal = false
if (action) {
this.$navigateBack()
this.releaseBackEvent()
}
})
} else {
this.$navigateBack()
this.releaseBackEvent()
}
},
hijackBackEvent() {
2020-10-22 18:36:50 +00:00
AndroidApplication.on(
AndroidApplication.activityBackPressedEvent,
2020-10-21 17:54:45 +00:00
this.backEvent
)
},
releaseBackEvent() {
2020-10-22 18:36:50 +00:00
AndroidApplication.off(
AndroidApplication.activityBackPressedEvent,
2020-10-21 17:54:45 +00:00
this.backEvent
)
},
backEvent(args) {
if (this.hasEnoughDetails && !this.blockModal) {
args.cancel = true
this.navigateBack()
}
2020-10-14 19:32:32 +00:00
},
takePicture() {
2020-10-26 20:49:54 +00:00
const vm = this
camera.requestPermissions().then(
() => {
camera
.takePicture({
width: vm.screenWidth,
height: vm.screenWidth,
keepAspectRatio: false,
saveToGallery: false,
})
.then((imageAsset) => {
let result = imageAsset._android
ImageSource.fromFile(result).then((savedImg) => {
let savedImgPath = path.join(
knownFolders.documents().getFolder("enrecipes").path,
`${vm.getRandomID()}.jpg`
)
savedImg.saveToFile(savedImgPath, "jpg")
vm.recipeContent.imageSrc = savedImgPath
})
})
.catch((err) => {
console.log("Error -> " + err.message)
})
2020-10-14 19:32:32 +00:00
},
2020-10-26 20:49:54 +00:00
() => {
console.log("permission request rejected")
}
)
},
selectPicture() {
let context = imagepicker.create({
mode: "single",
mediaType: "Image",
2020-10-14 19:32:32 +00:00
})
2020-10-26 20:49:54 +00:00
context
.authorize()
.then(() => context.present())
.then((selection) => {
let result = selection[0]._android
ImageSource.fromFile(result).then((savedImg) => {
let savedImgPath = path.join(
knownFolders.documents().getFolder("enrecipes").path,
`${this.getRandomID()}.jpg`
)
savedImg.saveToFile(savedImgPath, "jpg")
this.recipeContent.imageSrc = savedImgPath
})
})
.catch(function(e) {
console.log(e)
})
2020-10-14 19:32:32 +00:00
},
removePicture() {
confirm({
2020-10-21 17:54:45 +00:00
title: "Delete Photo",
2020-10-14 19:32:32 +00:00
message: "Are you sure you want to delete the recipe photo?",
okButtonText: "Delete",
cancelButtonText: "Cancel",
}).then((e) => {
if (e) {
this.recipeContent.imageSrc = null
}
2020-10-14 19:32:32 +00:00
})
},
addIngredient() {
this.recipeContent.ingredients.push({
2020-10-21 17:54:45 +00:00
item: "",
2020-10-14 19:32:32 +00:00
quantity: null,
unit: "unit",
})
},
removeIngredient(index) {
this.recipeContent.ingredients.splice(index, 1)
},
addInstruction() {
this.recipeContent.instructions.push("")
},
removeInstruction(index) {
this.recipeContent.instructions.splice(index, 1)
},
addNote() {
this.recipeContent.notes.push("")
},
removeNote(index) {
this.recipeContent.notes.splice(index, 1)
},
addReference() {
this.recipeContent.references.push("")
},
removeReference(index) {
this.recipeContent.references.splice(index, 1)
},
showUnits(e) {
2020-10-21 17:54:45 +00:00
this.releaseBackEvent()
this.$showModal(ActionDialog, {
props: {
title: "Unit",
list: [...this.units],
height: "75%",
},
}).then((action) => {
this.hijackBackEvent()
if (action) e.object.text = action
})
2020-10-14 19:32:32 +00:00
},
},
2020-10-26 20:49:54 +00:00
created() {
setTimeout((e) => {
this.setCurrentComponentAction("EditRecipe")
}, 500)
this.title = this.recipeID ? "Edit recipe" : "New recipe"
if (this.recipeID) {
let recipe = this.recipes.filter((e) => e.id === this.recipeID)[0]
Object.assign(this.recipeContent, recipe)
Object.assign(this.tempRecipeContent, recipe)
} else {
if (this.selectedCategory)
this.recipeContent.category = this.selectedCategory
this.newRecipeID = this.getRandomID()
}
this.hijackBackEvent()
},
2020-10-14 19:32:32 +00:00
}
</script>