before ns migrate
|
@ -11,7 +11,7 @@
|
|||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdkVersion 17
|
||||
minSdkVersion 23
|
||||
generatedDensities = []
|
||||
}
|
||||
aaptOptions {
|
||||
|
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 550 B |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 461 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 664 B |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 472 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 780 B |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 5.6 KiB |
|
@ -1,10 +1,11 @@
|
|||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:gravity="fill">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/white" />
|
||||
<!-- <solid android:color="@android:color/white" /> -->
|
||||
<solid android:color="#ff7043" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<item android:left="64dp" android:right="64dp">
|
||||
<bitmap android:gravity="center" android:src="@drawable/logo" />
|
||||
</item>
|
||||
</layer-list>
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 680 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 861 B |
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 10 KiB |
|
@ -1,4 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ns_accent">#ff7043</color>
|
||||
<color name="ns_primary">
|
||||
#FFFFFF
|
||||
</color>
|
||||
<color name="ns_primaryDark">
|
||||
#ff7043
|
||||
</color>
|
||||
<color name="ns_accent">
|
||||
#ff7043
|
||||
</color>
|
||||
</resources>
|
|
@ -1,23 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- Application theme -->
|
||||
<style name="AppTheme" parent="AppThemeBase">
|
||||
<item name="android:datePickerStyle">@style/SpinnerDatePicker</item>
|
||||
<item name="android:timePickerStyle">@style/SpinnerTimePicker</item>
|
||||
<item name="android:datePickerStyle">
|
||||
@style/SpinnerDatePicker
|
||||
</item>
|
||||
<item name="android:timePickerStyle">
|
||||
@style/SpinnerTimePicker
|
||||
</item>
|
||||
</style>
|
||||
|
||||
<!-- Default style for DatePicker - in spinner mode -->
|
||||
<style name="SpinnerDatePicker" parent="android:Widget.Material.Light.DatePicker">
|
||||
<item name="android:datePickerMode">spinner</item>
|
||||
<item name="android:datePickerMode">
|
||||
spinner
|
||||
</item>
|
||||
</style>
|
||||
|
||||
<!-- Default style for TimePicker - in spinner mode -->
|
||||
<style name="SpinnerTimePicker" parent="android:Widget.Material.Light.TimePicker">
|
||||
<item name="android:timePickerMode">spinner</item>
|
||||
<item name="android:timePickerMode">
|
||||
spinner
|
||||
</item>
|
||||
</style>
|
||||
|
||||
<style name="NativeScriptToolbarStyle" parent="NativeScriptToolbarStyleBase">
|
||||
<item name="android:elevation">4dp</item>
|
||||
<item name="android:elevation">
|
||||
0dp
|
||||
</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -9,7 +9,4 @@
|
|||
<color name="ns_accent">
|
||||
#ff7043
|
||||
</color>
|
||||
<color name="ns_blue">
|
||||
#272734
|
||||
</color>
|
||||
</resources>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">NativeScript-Vue Application</string>
|
||||
<string name="title_activity_kimera">NativeScript-Vue Application</string>
|
||||
<string name="app_name">EnRecipes</string>
|
||||
<string name="title_activity_kimera">EnRecipes</string>
|
||||
</resources>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<item name="android:windowBackground">@drawable/splash_screen</item>
|
||||
|
||||
<item name="android:windowActionBarOverlay">true</item>
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
</style>
|
||||
|
||||
<style name="LaunchScreenTheme" parent="LaunchScreenThemeBase">
|
||||
|
|
63
app/app.scss
|
@ -37,7 +37,8 @@ Page {
|
|||
.ns-light {
|
||||
Page,
|
||||
ActionBar,
|
||||
SearchBar {
|
||||
SearchBar,
|
||||
TabView {
|
||||
background: $grayL4;
|
||||
color: $grayD4;
|
||||
}
|
||||
|
@ -58,8 +59,10 @@ Page {
|
|||
.fieldLabel {
|
||||
background: $grayL4;
|
||||
}
|
||||
.recipe-li,
|
||||
.option-highlight {
|
||||
background: $grayL2;
|
||||
}
|
||||
.recipe-li {
|
||||
background: white;
|
||||
}
|
||||
.sd-item,
|
||||
|
@ -102,7 +105,8 @@ Page {
|
|||
.ns-dark {
|
||||
Page,
|
||||
ActionBar,
|
||||
SearchBar {
|
||||
SearchBar,
|
||||
TabView {
|
||||
background: $grayD4;
|
||||
color: $grayL4;
|
||||
}
|
||||
|
@ -169,9 +173,10 @@ Page {
|
|||
TextField,
|
||||
TextView,
|
||||
TimePickerField {
|
||||
width: 100%;
|
||||
border-width: 1;
|
||||
font-size: 15;
|
||||
padding: 16;
|
||||
font-size: 14;
|
||||
padding: 14;
|
||||
margin: 8 0 0 0;
|
||||
border-radius: 4;
|
||||
placeholder-color: $gray;
|
||||
|
@ -195,7 +200,8 @@ ActionBar {
|
|||
padding: 0;
|
||||
height: 64;
|
||||
.bx {
|
||||
padding: 16;
|
||||
padding: 16 12;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
.leftAction {
|
||||
padding: 16 16 16 4;
|
||||
|
@ -207,8 +213,8 @@ ActionBar {
|
|||
padding: 0;
|
||||
}
|
||||
SearchBar {
|
||||
width: 100%;
|
||||
font-size: 16;
|
||||
margin-top: 4;
|
||||
}
|
||||
.title {
|
||||
padding-left: 8;
|
||||
|
@ -219,15 +225,25 @@ SearchBar {
|
|||
// Side Drawer
|
||||
.sd-item {
|
||||
border-radius: 4;
|
||||
padding: 12 16;
|
||||
font-size: 14;
|
||||
padding: 0 16;
|
||||
height: 48;
|
||||
font-size: 16;
|
||||
vertical-alignment: center;
|
||||
// prettier-ignore
|
||||
.bx, Label {
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
.sd-group-header {
|
||||
padding: 12;
|
||||
width: 100%;
|
||||
padding: 8 8 16;
|
||||
font-size: 12;
|
||||
}
|
||||
|
||||
// Home
|
||||
RadListView {
|
||||
margin-bottom: 128;
|
||||
}
|
||||
.recipe-li {
|
||||
margin: 8 16;
|
||||
border-radius: 6;
|
||||
|
@ -235,6 +251,7 @@ SearchBar {
|
|||
margin: 4 0;
|
||||
}
|
||||
.recipe-cat {
|
||||
font-size: 12;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -248,21 +265,18 @@ SearchBar {
|
|||
padding: 0;
|
||||
}
|
||||
.recipe-favorite {
|
||||
font-size: 14;
|
||||
padding: 14 12 0 0;
|
||||
font-size: 12;
|
||||
padding: 14 8 0 0;
|
||||
}
|
||||
.recipe-cat,
|
||||
.recipe-favorite {
|
||||
color: $orange;
|
||||
}
|
||||
.recipe-favorite.hide {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Settings
|
||||
.group-header {
|
||||
padding: 12;
|
||||
padding: 8;
|
||||
color: #ff7043;
|
||||
}
|
||||
.main-container {
|
||||
|
@ -317,22 +331,27 @@ SearchBar {
|
|||
}
|
||||
.view-title {
|
||||
font-size: 22;
|
||||
line-height: 6;
|
||||
margin-bottom: 16;
|
||||
}
|
||||
.view-ingredient {
|
||||
font-size: 14;
|
||||
line-height: 6;
|
||||
margin-top: 12;
|
||||
padding-bottom: 16;
|
||||
}
|
||||
.view-favorited {
|
||||
color: #ff7043;
|
||||
}
|
||||
.activity-indicator {
|
||||
background: #ff7043;
|
||||
}
|
||||
|
||||
.view-count {
|
||||
font-size: 10;
|
||||
width: 20;
|
||||
height: 20;
|
||||
padding: 3 0 0;
|
||||
margin-left: 6;
|
||||
padding-top: 3%;
|
||||
margin: 0 0 0 6;
|
||||
text-align: center;
|
||||
border-radius: 100;
|
||||
}
|
||||
|
@ -342,7 +361,7 @@ SearchBar {
|
|||
.view-reference {
|
||||
font-size: 14;
|
||||
line-height: 6;
|
||||
padding: 0 0 12 24;
|
||||
padding: 0 0 16 24;
|
||||
margin: 0 0 0 15;
|
||||
border-width: 0 0 0 2;
|
||||
}
|
||||
|
@ -368,3 +387,7 @@ SearchBar {
|
|||
padding: 16;
|
||||
margin: 8 0 0 0;
|
||||
}
|
||||
.closeBtn {
|
||||
padding: 4;
|
||||
margin-top: 16;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Page>
|
||||
<Page @loaded="setCurrentComponent">
|
||||
<ActionBar :flat="viewIsScrolled ? false : true">
|
||||
<!-- Settings Actionbar -->
|
||||
<GridLayout rows="*" columns="auto, *" class="actionBarContainer">
|
||||
|
@ -10,7 +10,7 @@
|
|||
@tap="showDrawer"
|
||||
col="0"
|
||||
/>
|
||||
<Label class="title orkm" :text="title" col="1" />
|
||||
<Label class="title orkm" text="About" col="1" />
|
||||
</GridLayout>
|
||||
</ActionBar>
|
||||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
|
@ -85,9 +85,12 @@ import { mapState, mapActions } from "vuex"
|
|||
export default {
|
||||
props: ["highlight", "viewIsScrolled", "showDrawer", "title"],
|
||||
computed: {
|
||||
...mapState(["icon"]),
|
||||
...mapState(["icon",'currentComponent']),
|
||||
},
|
||||
methods: {
|
||||
setCurrentComponent() {
|
||||
this.$store.dispatch("setCurrentComponent", "About")
|
||||
},
|
||||
openURL(args, url) {
|
||||
this.highlight(args)
|
||||
utils.openUrl(url)
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
drawerContentSize="270"
|
||||
showOverNavigation="true"
|
||||
gesturesEnabled="true"
|
||||
drawerTransition="RevealTransition"
|
||||
drawerTransition="SlideInOnTopTransition"
|
||||
>
|
||||
<GridLayout
|
||||
rows="auto, auto, *, auto, auto"
|
||||
rows="*, auto"
|
||||
columns="*"
|
||||
~drawerContent
|
||||
padding="8"
|
||||
|
@ -17,62 +17,65 @@
|
|||
>
|
||||
<StackLayout row="0">
|
||||
<StackLayout
|
||||
@tap="localNavigation('EnRecipes', 'EnRecipes', true, false)"
|
||||
v-for="(item, index) in topmenu"
|
||||
:key="index"
|
||||
@tap="navigateTo(item.component, false, false)"
|
||||
orientation="horizontal"
|
||||
class="sd-item orkm"
|
||||
:class="{
|
||||
'selected-sd-item':
|
||||
currentComponent === 'EnRecipes' &&
|
||||
!filterFavorites &&
|
||||
!selectedCategory,
|
||||
'selected-sd-item': currentComponent === item.component,
|
||||
}"
|
||||
>
|
||||
<Label class="bx" :text="icon.home" margin="0 24 0 0" />
|
||||
<Label verticalAlignment="center" text="Home" />
|
||||
<Label class="bx" :text="icon[item.icon]" margin="0 24 0 0" />
|
||||
<Label :text="item.title" />
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
@tap="localNavigation('Favorites', 'Favorites', true, false)"
|
||||
orientation="horizontal"
|
||||
class="sd-item orkm"
|
||||
:class="{
|
||||
'selected-sd-item':
|
||||
currentComponent === 'EnRecipes' && filterFavorites,
|
||||
}"
|
||||
<StackLayout class="hr m-10"></StackLayout>
|
||||
<GridLayout
|
||||
class="sd-group-header orkm"
|
||||
rows="auto"
|
||||
columns="*, auto"
|
||||
>
|
||||
<Label class="bx" :text="icon.heart" margin="0 24 0 0" />
|
||||
<Label verticalAlignment="center" text="Favorites" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
orientation="horizontal"
|
||||
row="1"
|
||||
class="sd-group-header orkr"
|
||||
>
|
||||
<Label text="Categories" />
|
||||
</StackLayout>
|
||||
<ScrollView row="2" scrollBarIndicatorVisible="false">
|
||||
<Label col="0" text="Categories" />
|
||||
<Label
|
||||
@tap="toggleCatEdit"
|
||||
col="2"
|
||||
:text="catEditMode ? 'DONE' : 'RENAME'"
|
||||
/>
|
||||
</GridLayout>
|
||||
<ScrollView height="100%">
|
||||
<StackLayout>
|
||||
<StackLayout
|
||||
@tap="localNavigation(item, item, false, true)"
|
||||
<GridLayout
|
||||
@tap="navigateTo(item, false, true)"
|
||||
v-for="(item, index) in categories"
|
||||
:key="index"
|
||||
orientation="horizontal"
|
||||
class="sd-item orkm"
|
||||
:class="{
|
||||
'selected-sd-item':
|
||||
currentComponent === 'EnRecipes' && selectedCategory == item,
|
||||
'selected-sd-item': currentComponent == item,
|
||||
}"
|
||||
columns="auto, *, auto"
|
||||
>
|
||||
<Label class="bx" :text="icon.label" margin="0 24 0 0" />
|
||||
<Label verticalAlignment="center" :text="item" />
|
||||
</StackLayout>
|
||||
<Label
|
||||
col="0"
|
||||
class="bx"
|
||||
:text="icon.label"
|
||||
margin="0 24 0 0"
|
||||
/>
|
||||
<Label col="1" :text="item" />
|
||||
<Label
|
||||
v-if="catEditMode"
|
||||
@tap="editCategory(item)"
|
||||
col="2"
|
||||
class="bx"
|
||||
:text="icon.edit"
|
||||
/>
|
||||
</GridLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
|
||||
<StackLayout row="3" class="hr m-10"></StackLayout>
|
||||
<StackLayout row="4">
|
||||
</StackLayout>
|
||||
<StackLayout row="1">
|
||||
<StackLayout class="hr m-10"></StackLayout>
|
||||
<StackLayout
|
||||
@tap="navigateTo(item.component, item.title)"
|
||||
@tap="navigateTo(item.component, true, false)"
|
||||
v-for="(item, index) in bottommenu"
|
||||
:key="index"
|
||||
orientation="horizontal"
|
||||
|
@ -82,19 +85,30 @@
|
|||
}"
|
||||
>
|
||||
<Label class="bx" :text="icon[item.icon]" margin="0 24 0 0" />
|
||||
<Label verticalAlignment="center" :text="item.title" />
|
||||
<Label :text="item.title" />
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
orientation="horizontal"
|
||||
class="sd-item orkm"
|
||||
@tap="donate"
|
||||
>
|
||||
<Label class="bx" :text="icon.donate" margin="0 24 0 0" />
|
||||
<Label text="Donate" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</GridLayout>
|
||||
|
||||
<GridLayout ~mainContent rows="*" columns="*">
|
||||
<Frame id="main-frame">
|
||||
<Frame ref="mainFrame" id="main-frame">
|
||||
<!-- Home -->
|
||||
<EnRecipes
|
||||
:selectedCategory="selectedCategory"
|
||||
ref="enrecipes"
|
||||
:filterFavorites="filterFavorites"
|
||||
:title="title"
|
||||
:filterMustTry="filterMustTry"
|
||||
:selectedCategory="selectedCategory"
|
||||
:showDrawer="showDrawer"
|
||||
:hijackGlobalBackEvent="hijackGlobalBackEvent"
|
||||
:releaseGlobalBackEvent="releaseGlobalBackEvent"
|
||||
/>
|
||||
</Frame>
|
||||
</GridLayout>
|
||||
|
@ -104,15 +118,20 @@
|
|||
|
||||
<script>
|
||||
import * as utils from "tns-core-modules/utils/utils"
|
||||
import { isAndroid } from "tns-core-modules/platform"
|
||||
import * as application from "tns-core-modules/application"
|
||||
|
||||
import { getString } from "application-settings"
|
||||
import Theme from "@nativescript/theme"
|
||||
import * as Toast from "nativescript-toast"
|
||||
|
||||
import EnRecipes from "./EnRecipes.vue"
|
||||
import Settings from "./Settings.vue"
|
||||
import About from "./About.vue"
|
||||
|
||||
import PromptDialog from "./modal/PromptDialog.vue"
|
||||
import { mapState } from "vuex"
|
||||
|
||||
import { Couchbase, ConcurrencyMode } from "nativescript-couchbase-plugin"
|
||||
|
||||
let page
|
||||
export default {
|
||||
components: {
|
||||
|
@ -122,12 +141,26 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
title: "EnRecipes",
|
||||
selectedCategory: null,
|
||||
filterFavorites: false,
|
||||
searchQuery: "",
|
||||
showSearch: false,
|
||||
currentComponent: "EnRecipes",
|
||||
filterMustTry: false,
|
||||
topmenu: [
|
||||
{
|
||||
title: "Home",
|
||||
component: "EnRecipes",
|
||||
icon: "home",
|
||||
},
|
||||
{
|
||||
title: "Favorites",
|
||||
component: "Favorites",
|
||||
icon: "heart",
|
||||
},
|
||||
{
|
||||
title: "Must-Try",
|
||||
component: "Must-Try",
|
||||
icon: "musttry",
|
||||
},
|
||||
],
|
||||
bottommenu: [
|
||||
{
|
||||
title: "Settings",
|
||||
|
@ -140,10 +173,11 @@ export default {
|
|||
icon: "info",
|
||||
},
|
||||
],
|
||||
catEditMode: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["recipes", "icon"]),
|
||||
...mapState(["recipes", "categories", "icon", "currentComponent"]),
|
||||
categories() {
|
||||
let arr = this.recipes.map((e) => {
|
||||
return e.category
|
||||
|
@ -152,6 +186,39 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
toggleCatEdit() {
|
||||
this.catEditMode = !this.catEditMode
|
||||
this.setComponent("EnRecipes")
|
||||
this.filterFavorites = this.filterMustTry = false
|
||||
this.selectedCategory = null
|
||||
this.$refs.enrecipes.updateFilter()
|
||||
},
|
||||
setComponent(comp) {
|
||||
this.$store.dispatch("setCurrentComponent", comp)
|
||||
},
|
||||
editCategory(item) {
|
||||
this.releaseGlobalBackEvent()
|
||||
this.$showModal(PromptDialog, {
|
||||
props: {
|
||||
title: `Rename category`,
|
||||
hint: item,
|
||||
action: "RENAME",
|
||||
},
|
||||
}).then((result) => {
|
||||
this.hijackGlobalBackEvent()
|
||||
if (result.length) {
|
||||
if (this.categories.includes(result)) {
|
||||
Toast.makeText("Category already exists!", "long").show()
|
||||
} else {
|
||||
this.$store.dispatch("renameCategory", {
|
||||
current: item,
|
||||
updated: result,
|
||||
})
|
||||
this.catEditMode = false
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
highlight(args) {
|
||||
let temp = args.object.className
|
||||
args.object.className = `${temp} option-highlight`
|
||||
|
@ -159,70 +226,103 @@ export default {
|
|||
args.object.className = temp
|
||||
}, 100)
|
||||
},
|
||||
|
||||
// Navigation
|
||||
setSelectedCategory(e) {
|
||||
this.selectedCategory = e.item
|
||||
this.closeDrawer()
|
||||
},
|
||||
removeBackEvent() {
|
||||
application.android.off(
|
||||
application.AndroidApplication.activityBackPressedEvent,
|
||||
this.backEvent
|
||||
)
|
||||
},
|
||||
backEvent(args) {
|
||||
args.cancel = true
|
||||
if (this.$refs.drawer.nativeView.getIsOpen()) this.closeDrawer()
|
||||
else if (this.currentComponent !== "EnRecipes") {
|
||||
this.$navigateBack({ frame: "main-frame" })
|
||||
this.currentComponent = "EnRecipes"
|
||||
} else if (this.filterFavorites || this.selectedCategory) {
|
||||
this.title = "EnRecipes"
|
||||
this.filterFavorites = false
|
||||
this.selectedCategory = null
|
||||
this.removeBackEvent()
|
||||
}
|
||||
},
|
||||
localNavigation(to, title, filter, filterCategory) {
|
||||
// navigateBackToHome
|
||||
hijackGlobalBackEvent() {
|
||||
application.android.on(
|
||||
application.AndroidApplication.activityBackPressedEvent,
|
||||
this.backEvent
|
||||
this.globalBackEvent
|
||||
)
|
||||
if (this.currentComponent !== "EnRecipes") {
|
||||
this.currentComponent = "EnRecipes"
|
||||
this.$navigateBack({ frame: "main-frame" })
|
||||
}
|
||||
this.filterFavorites = false
|
||||
this.selectedCategory = null
|
||||
this.title = title
|
||||
console.log(title)
|
||||
if (filter) {
|
||||
if (to === "Favorites") this.filterFavorites = true
|
||||
} else if (filterCategory) this.selectedCategory = to
|
||||
if (!this.filterFavorites && !this.selectedCategory)
|
||||
this.removeBackEvent()
|
||||
this.closeDrawer()
|
||||
},
|
||||
navigateTo(to, title) {
|
||||
this.currentComponent = title
|
||||
releaseGlobalBackEvent() {
|
||||
application.android.off(
|
||||
application.AndroidApplication.activityBackPressedEvent,
|
||||
this.globalBackEvent
|
||||
)
|
||||
},
|
||||
globalBackEvent(args) {
|
||||
function preventDefault() {
|
||||
args.cancel = true
|
||||
}
|
||||
let vm = this
|
||||
function isFiltered() {
|
||||
vm.filterFavorites
|
||||
? vm.setComponent("Favorites")
|
||||
: vm.filterMustTry
|
||||
? vm.setComponent("Must-Try")
|
||||
: vm.selectedCategory
|
||||
? vm.setComponent(vm.selectedCategory)
|
||||
: vm.setComponent("EnRecipes")
|
||||
}
|
||||
if (this.$refs.drawer && this.$refs.drawer.nativeView.getIsOpen()) {
|
||||
preventDefault()
|
||||
this.closeDrawer()
|
||||
this.catEditMode = false
|
||||
} else if (
|
||||
["Favorites", "Must-Try", this.selectedCategory].includes(
|
||||
this.currentComponent
|
||||
)
|
||||
) {
|
||||
preventDefault()
|
||||
this.setComponent("EnRecipes")
|
||||
this.filterFavorites = this.filterMustTry = false
|
||||
this.selectedCategory = null
|
||||
this.$refs.enrecipes.updateFilter()
|
||||
this.releaseGlobalBackEvent()
|
||||
}
|
||||
},
|
||||
navigateTo(to, isTrueComponent, isCategory) {
|
||||
if (isTrueComponent) {
|
||||
this.$navigateTo(to, {
|
||||
frame: "main-frame",
|
||||
// transition: {
|
||||
// name: "slide",
|
||||
// duration: 250,
|
||||
// curve: "easeIn",
|
||||
// },
|
||||
props: {
|
||||
highlight: this.highlight,
|
||||
viewIsScrolled: this.viewIsScrolled,
|
||||
showDrawer: this.showDrawer,
|
||||
title,
|
||||
restartApp: this.restartApp,
|
||||
hijackGlobalBackEvent: this.hijackGlobalBackEvent,
|
||||
releaseGlobalBackEvent: this.releaseGlobalBackEvent,
|
||||
},
|
||||
backstackVisible: false,
|
||||
})
|
||||
this.closeDrawer()
|
||||
} else if (!this.catEditMode) {
|
||||
this.releaseGlobalBackEvent()
|
||||
this.hijackGlobalBackEvent()
|
||||
this.setComponent(to)
|
||||
this.$navigateBack({ frame: "main-frame", backstackVisible: false })
|
||||
this.filterFavorites = to === "Favorites" ? true : false
|
||||
this.filterMustTry = to === "Must-Try" ? true : false
|
||||
this.selectedCategory = isCategory ? to : null
|
||||
this.$refs.enrecipes.updateFilter()
|
||||
this.closeDrawer()
|
||||
}
|
||||
},
|
||||
restartApp() {
|
||||
// Code from nativescript-master-technology
|
||||
const mStartActivity = new android.content.Intent(
|
||||
application.android.context,
|
||||
application.android.startActivity.getClass()
|
||||
)
|
||||
const mPendingIntentId = parseInt(Math.random() * 100000, 10)
|
||||
const mPendingIntent = android.app.PendingIntent.getActivity(
|
||||
application.android.context,
|
||||
mPendingIntentId,
|
||||
mStartActivity,
|
||||
android.app.PendingIntent.FLAG_CANCEL_CURRENT
|
||||
)
|
||||
const mgr = application.android.context.getSystemService(
|
||||
android.content.Context.ALARM_SERVICE
|
||||
)
|
||||
mgr.set(
|
||||
android.app.AlarmManager.RTC,
|
||||
java.lang.System.currentTimeMillis() + 100,
|
||||
mPendingIntent
|
||||
)
|
||||
android.os.Process.killProcess(android.os.Process.myPid())
|
||||
},
|
||||
showDrawer() {
|
||||
this.$refs.drawer.nativeView.showDrawer()
|
||||
|
@ -230,6 +330,14 @@ export default {
|
|||
closeDrawer() {
|
||||
this.$refs.drawer.nativeView.closeDrawer()
|
||||
},
|
||||
donate(args) {
|
||||
this.highlight(args)
|
||||
utils.openUrl("https://www.vishnuraghav.com/donate/")
|
||||
},
|
||||
},
|
||||
created() {
|
||||
let themeName = getString("application-theme", "Light")
|
||||
Theme.setMode(Theme[themeName])
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Page>
|
||||
<Page @loaded="setCurrentComponent" @unloaded="releaseBackEvent">
|
||||
<ActionBar :flat="viewIsScrolled ? false : true">
|
||||
<GridLayout rows="*" columns="auto, *, auto," class="actionBarContainer">
|
||||
<Label
|
||||
|
@ -9,7 +9,8 @@
|
|||
col="0"
|
||||
@tap="navigateBack"
|
||||
/>
|
||||
<Label class="title orkm" text="New recipe" col="1" />
|
||||
<Label class="title orkm" :text="title" col="1" />
|
||||
|
||||
<Label
|
||||
v-if="hasEnoughDetails"
|
||||
class="bx"
|
||||
|
@ -46,7 +47,7 @@
|
|||
horizontalAlignment="center"
|
||||
class="bx"
|
||||
fontSize="160"
|
||||
:text="icon.dish"
|
||||
:text="icon.image"
|
||||
/>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
|
@ -75,17 +76,14 @@
|
|||
<StackLayout margin="0 16">
|
||||
<AbsoluteLayout class="inputField">
|
||||
<TextField
|
||||
width="100%"
|
||||
hint="My Healthy Recipe"
|
||||
v-model="recipeContent.title"
|
||||
autocapitalizationType="words"
|
||||
maxLength="32"
|
||||
/>
|
||||
<Label top="0" class="fieldLabel" text="Title" />
|
||||
</AbsoluteLayout>
|
||||
<AbsoluteLayout class="inputField">
|
||||
<TextField
|
||||
width="100%"
|
||||
v-model="recipeContent.category"
|
||||
editable="false"
|
||||
@tap="showCategories()"
|
||||
|
@ -95,7 +93,6 @@
|
|||
<GridLayout columns="*, 8, *">
|
||||
<AbsoluteLayout class="inputField" col="0">
|
||||
<TimePickerField
|
||||
width="100%"
|
||||
timeFormat="HH:mm"
|
||||
pickerTitle="Approx. preparation time"
|
||||
@timeChange="onPrepTimeChange"
|
||||
|
@ -105,7 +102,6 @@
|
|||
</AbsoluteLayout>
|
||||
<AbsoluteLayout class="inputField" col="2">
|
||||
<TimePickerField
|
||||
width="100%"
|
||||
timeFormat="HH:mm"
|
||||
pickerTitle="Approx. cooking time"
|
||||
@timeChange="onCookTimeChange"
|
||||
|
@ -157,11 +153,8 @@
|
|||
@tap="showUnits($event)"
|
||||
/>
|
||||
<Label
|
||||
verticalAlignment="center"
|
||||
col="6"
|
||||
padding="4"
|
||||
margin="8 0 0 0"
|
||||
class="bx"
|
||||
class="bx closeBtn"
|
||||
:text="icon.close"
|
||||
@tap="removeIngredient(index)"
|
||||
/>
|
||||
|
@ -189,18 +182,9 @@
|
|||
v-model="recipeContent.instructions[index]"
|
||||
editable="true"
|
||||
/>
|
||||
|
||||
<!-- <TextField
|
||||
col="0"
|
||||
v-model="recipeContent.instructions[index]"
|
||||
:hint="`Step ${index + 1}`"
|
||||
/> -->
|
||||
<Label
|
||||
verticalAlignment="center"
|
||||
col="2"
|
||||
padding="4"
|
||||
margin="8 0 0 0"
|
||||
class="bx"
|
||||
class="bx closeBtn"
|
||||
:text="icon.close"
|
||||
@tap="removeInstruction(index)"
|
||||
/>
|
||||
|
@ -228,11 +212,8 @@
|
|||
editable="true"
|
||||
/>
|
||||
<Label
|
||||
verticalAlignment="center"
|
||||
col="2"
|
||||
padding="4"
|
||||
margin="8 0 0 0"
|
||||
class="bx"
|
||||
class="bx closeBtn"
|
||||
:text="icon.close"
|
||||
@tap="removeNote(index)"
|
||||
/>
|
||||
|
@ -259,11 +240,8 @@
|
|||
hint="Website or Video URL"
|
||||
/>
|
||||
<Label
|
||||
verticalAlignment="center"
|
||||
col="2"
|
||||
padding="4"
|
||||
margin="8 0 0 0"
|
||||
class="bx"
|
||||
class="bx closeBtn"
|
||||
:text="icon.close"
|
||||
@tap="removeReference(index)"
|
||||
/>
|
||||
|
@ -284,32 +262,17 @@
|
|||
import { screen } from "tns-core-modules/platform"
|
||||
import { Mediafilepicker } from "nativescript-mediafilepicker"
|
||||
import { mapState, mapActions } from "vuex"
|
||||
import * as application from "tns-core-modules/application"
|
||||
import ActionDialog from "./modal/ActionDialog.vue"
|
||||
import PromptDialog from "./modal/PromptDialog.vue"
|
||||
import ConfirmDialog from "./modal/ConfirmDialog.vue"
|
||||
|
||||
export default {
|
||||
props: ["recipeIndex", "selectedCategory"],
|
||||
data() {
|
||||
return {
|
||||
title: "New recipe",
|
||||
viewIsScrolled: false,
|
||||
units: [
|
||||
"unit",
|
||||
"tsp",
|
||||
"Tbsp",
|
||||
"oz",
|
||||
"cup",
|
||||
"pt",
|
||||
"qt",
|
||||
"lb",
|
||||
"gal",
|
||||
"ml",
|
||||
"L",
|
||||
"mg",
|
||||
"g",
|
||||
"kg",
|
||||
"mm",
|
||||
"cm",
|
||||
"m",
|
||||
"in",
|
||||
"°C",
|
||||
"°F",
|
||||
],
|
||||
recipeContent: {
|
||||
imageSrc: null,
|
||||
title: null,
|
||||
|
@ -319,7 +282,7 @@ export default {
|
|||
portionSize: 1,
|
||||
ingredients: [
|
||||
{
|
||||
item: null,
|
||||
item: "",
|
||||
quantity: null,
|
||||
unit: "unit",
|
||||
},
|
||||
|
@ -328,71 +291,97 @@ export default {
|
|||
notes: [""],
|
||||
references: [""],
|
||||
isFavorite: false,
|
||||
tried: false,
|
||||
lastModified: null,
|
||||
},
|
||||
categories: [
|
||||
"Appetizers",
|
||||
"BBQ",
|
||||
"Beverages",
|
||||
"Breads",
|
||||
"Breakfast",
|
||||
"Desserts",
|
||||
"Dinner",
|
||||
"Drinks",
|
||||
"Healthy",
|
||||
"Lunch",
|
||||
"Main dishes",
|
||||
"Meat",
|
||||
"Noodles",
|
||||
"Pasta",
|
||||
"Poultry",
|
||||
"Rice",
|
||||
"Salads",
|
||||
"Sauces",
|
||||
"Seafood",
|
||||
"Side dishes",
|
||||
"Snacks",
|
||||
"Soups",
|
||||
"Vegan",
|
||||
"Vegetarian",
|
||||
"ADD NEW CATEGORY",
|
||||
],
|
||||
tempRecipeContent: {},
|
||||
blockModal: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["icon"]),
|
||||
...mapState(["icon", "units", "categories", "currentComponent", "recipes"]),
|
||||
screenWidth() {
|
||||
return screen.mainScreen.widthDIPs
|
||||
},
|
||||
hasEnoughDetails() {
|
||||
let recipe = this.recipeContent
|
||||
return recipe.title && recipe.category
|
||||
if (this.recipeIndex) {
|
||||
return (
|
||||
JSON.stringify(this.recipeContent) !==
|
||||
JSON.stringify(this.tempRecipeContent)
|
||||
)
|
||||
} else {
|
||||
return this.recipeContent.title
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setTime(time) {
|
||||
if (Date.parse(this.recipeContent[time])) {
|
||||
let date = new Date(this.recipeContent[time])
|
||||
setCurrentComponent() {
|
||||
setTimeout((e) => {
|
||||
this.$store.dispatch("setCurrentComponent", "EditRecipe")
|
||||
}, 500)
|
||||
this.title = this.recipeIndex >= 0 ? "Edit recipe" : "New recipe"
|
||||
if (this.recipeIndex >= 0) {
|
||||
Object.assign(this.recipeContent, this.recipes[this.recipeIndex])
|
||||
Object.assign(this.tempRecipeContent, this.recipes[this.recipeIndex])
|
||||
} else {
|
||||
if (this.selectedCategory)
|
||||
this.recipeContent.category = this.selectedCategory
|
||||
Object.assign(this.tempRecipeContent, this.recipeContent)
|
||||
}
|
||||
this.hijackBackEvent()
|
||||
},
|
||||
setTime(key, time) {
|
||||
if (Date.parse(time)) {
|
||||
let date = new Date(time)
|
||||
let h = date.getHours()
|
||||
let m = date.getMinutes()
|
||||
|
||||
this.recipeContent[time] =
|
||||
this.recipeContent[key] =
|
||||
(h < 10 ? "0" + h : h) + ":" + (m < 10 ? "0" + m : m)
|
||||
}
|
||||
// console.log(this.recipeContent[time])
|
||||
},
|
||||
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")
|
||||
},
|
||||
saveRecipe() {
|
||||
this.setTime("prepTime")
|
||||
this.setTime("cookTime")
|
||||
// console.log(this.recipeContent)
|
||||
|
||||
this.clearEmptyFields()
|
||||
this.recipeContent.lastModified = new Date()
|
||||
if (this.recipeIndex >= 0) {
|
||||
this.$store.dispatch("overwriteRecipe", {
|
||||
index: this.recipeIndex,
|
||||
recipe: this.recipeContent,
|
||||
})
|
||||
} else {
|
||||
this.$store.dispatch("addRecipe", this.recipeContent)
|
||||
}
|
||||
this.$navigateBack()
|
||||
},
|
||||
onPrepTimeChange(args) {
|
||||
this.recipeContent.prepTime = args.value
|
||||
this.setTime("prepTime", args.value)
|
||||
},
|
||||
onCookTimeChange(args) {
|
||||
this.recipeContent.cookTime = args.value
|
||||
this.setTime("cookTime", args.value)
|
||||
},
|
||||
onScroll(args) {
|
||||
args.scrollY
|
||||
|
@ -400,25 +389,77 @@ export default {
|
|||
: (this.viewIsScrolled = false)
|
||||
},
|
||||
showCategories() {
|
||||
action("Select a category", "Cancel", [...this.categories]).then(
|
||||
(result) => {
|
||||
if (result != "Cancel") this.recipeContent.category = result
|
||||
}
|
||||
)
|
||||
this.releaseBackEvent()
|
||||
this.$showModal(ActionDialog, {
|
||||
props: {
|
||||
title: "Category",
|
||||
list: [...this.categories],
|
||||
height: "75%",
|
||||
action: "NEW CATEGORY",
|
||||
},
|
||||
navigateBack() {
|
||||
confirm({
|
||||
message:
|
||||
"Are you sure you want discard unsaved changes to this recipe?",
|
||||
cancelButtonText: "Keep Editing",
|
||||
okButtonText: "Discard",
|
||||
}).then((res) => {
|
||||
if (res) {
|
||||
this.$navigateBack()
|
||||
}).then((action) => {
|
||||
if (action == "NEW CATEGORY") {
|
||||
this.$showModal(PromptDialog, {
|
||||
props: {
|
||||
title: "New category",
|
||||
action: "ADD",
|
||||
},
|
||||
}).then((result) => {
|
||||
this.hijackBackEvent()
|
||||
if (result.length) {
|
||||
this.recipeContent.category = result
|
||||
this.$store.dispatch("addCategory", result)
|
||||
}
|
||||
})
|
||||
} else if (action) {
|
||||
this.recipeContent.category = action
|
||||
this.hijackBackEvent()
|
||||
} else {
|
||||
this.hijackBackEvent()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
navigateBack() {
|
||||
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() {
|
||||
application.android.on(
|
||||
application.AndroidApplication.activityBackPressedEvent,
|
||||
this.backEvent
|
||||
)
|
||||
},
|
||||
releaseBackEvent() {
|
||||
application.android.off(
|
||||
application.AndroidApplication.activityBackPressedEvent,
|
||||
this.backEvent
|
||||
)
|
||||
},
|
||||
backEvent(args) {
|
||||
if (this.hasEnoughDetails && !this.blockModal) {
|
||||
args.cancel = true
|
||||
this.navigateBack()
|
||||
}
|
||||
},
|
||||
takePicture() {
|
||||
let mediafilepicker = new Mediafilepicker()
|
||||
let vm = this
|
||||
|
@ -462,7 +503,7 @@ export default {
|
|||
},
|
||||
removePicture() {
|
||||
confirm({
|
||||
title: "Delete Recipe Photo",
|
||||
title: "Delete Photo",
|
||||
message: "Are you sure you want to delete the recipe photo?",
|
||||
okButtonText: "Delete",
|
||||
cancelButtonText: "Cancel",
|
||||
|
@ -473,7 +514,7 @@ export default {
|
|||
|
||||
addIngredient() {
|
||||
this.recipeContent.ingredients.push({
|
||||
item: null,
|
||||
item: "",
|
||||
quantity: null,
|
||||
unit: "unit",
|
||||
})
|
||||
|
@ -504,11 +545,17 @@ export default {
|
|||
},
|
||||
|
||||
showUnits(e) {
|
||||
action("Select measuring unit", "Cancel", [...this.units]).then(
|
||||
(result) => {
|
||||
if (result != "Cancel") e.object.text = result
|
||||
}
|
||||
)
|
||||
this.releaseBackEvent()
|
||||
this.$showModal(ActionDialog, {
|
||||
props: {
|
||||
title: "Unit",
|
||||
list: [...this.units],
|
||||
height: "75%",
|
||||
},
|
||||
}).then((action) => {
|
||||
this.hijackBackEvent()
|
||||
if (action) e.object.text = action
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<Page>
|
||||
<Page @loaded="setCurrentComponent">
|
||||
<ActionBar :flat="viewIsScrolled ? false : true">
|
||||
<!-- Search Actionbar -->
|
||||
<GridLayout
|
||||
v-if="showSearch"
|
||||
rows="*"
|
||||
columns="auto, *"
|
||||
class="actionBarContainer"
|
||||
verticalAlignment="center"
|
||||
>
|
||||
<Label
|
||||
class="bx leftAction"
|
||||
|
@ -15,20 +15,20 @@
|
|||
col="0"
|
||||
@tap="closeSearch"
|
||||
/>
|
||||
<!-- @loaded="searchBarLoaded" -->
|
||||
<SearchBar
|
||||
@loaded="searchBarLoaded"
|
||||
id="searchField"
|
||||
col="1"
|
||||
hint="Search"
|
||||
textFieldHintColor="#bdbdbd"
|
||||
v-model="searchQuery"
|
||||
@textChange="updateFilter"
|
||||
@clear="updateFilter"
|
||||
/>
|
||||
</GridLayout>
|
||||
<!-- Home Actionbar -->
|
||||
<GridLayout
|
||||
v-else
|
||||
rows="*"
|
||||
columns="auto, *, auto,"
|
||||
columns="auto, *, auto, auto"
|
||||
class="actionBarContainer"
|
||||
>
|
||||
<Label
|
||||
|
@ -38,84 +38,61 @@
|
|||
@tap="showDrawer"
|
||||
col="0"
|
||||
/>
|
||||
<Label class="title orkm" :text="title" col="1" />
|
||||
<Label
|
||||
class="bx"
|
||||
:text="icon.search"
|
||||
col="2"
|
||||
@tap="showSearch = true"
|
||||
/>
|
||||
<Label class="title orkm" :text="currentComponent" col="1" />
|
||||
<Label class="bx" :text="icon.search" col="2" @tap="openSearch" />
|
||||
<Label class="bx" :text="icon.sort" col="3" @tap="sortDialog" />
|
||||
</GridLayout>
|
||||
</ActionBar>
|
||||
<AbsoluteLayout>
|
||||
<RadListView
|
||||
v-if="filteredRecipes.length"
|
||||
ref="listView"
|
||||
for="recipe in filteredRecipes"
|
||||
itemHeight="112"
|
||||
for="recipe in recipes"
|
||||
swipeActions="true"
|
||||
@itemSwipeProgressChanged="onSwiping"
|
||||
@itemSwipeProgressEnded="onSwipeEnded"
|
||||
@scrolled="onScroll($event)"
|
||||
@itemTap="viewRecipe"
|
||||
:filteringFunction="filterFunction"
|
||||
:sortingFunction="sortFunction"
|
||||
>
|
||||
<v-template>
|
||||
<GridLayout
|
||||
class="recipe-li"
|
||||
rows="128"
|
||||
columns="auto, *, auto"
|
||||
androidElevation="2"
|
||||
rows="112"
|
||||
columns="112, *"
|
||||
androidElevation="1"
|
||||
>
|
||||
<Image
|
||||
src="res://icon"
|
||||
stretch="fill"
|
||||
col="0"
|
||||
width="128"
|
||||
height="128"
|
||||
/>
|
||||
<StackLayout
|
||||
class="recipe-info"
|
||||
col="1"
|
||||
horizontalAlignment="left"
|
||||
verticalAlignment="top"
|
||||
>
|
||||
<Label :text="recipe.category" class="orkm h4 recipe-cat" />
|
||||
<Image col="0" src="res://icon" stretch="fill" />
|
||||
<StackLayout class="recipe-info" col="1">
|
||||
<Label :text="recipe.category" class="orkm recipe-cat" />
|
||||
<Label :text="recipe.title" class="orkm recipe-title" />
|
||||
<Label
|
||||
:text="recipeTotalTime(recipe.prepTime, recipe.cookTime)"
|
||||
class="h4 recipe-time"
|
||||
/>
|
||||
</StackLayout>
|
||||
<Label
|
||||
verticalAlignment="top"
|
||||
col="2"
|
||||
class="bx recipe-favorite"
|
||||
:class="{ hide: !recipe.isFavorite }"
|
||||
:text="icon.heart"
|
||||
/>
|
||||
</GridLayout>
|
||||
</v-template>
|
||||
|
||||
<v-template name="itemswipe">
|
||||
<GridLayout columns="auto, *, auto">
|
||||
<StackLayout
|
||||
id="favorite-action"
|
||||
col="0"
|
||||
class="swipe-item left"
|
||||
verticalAlignment="top"
|
||||
>
|
||||
<Label class="bx" padding="8 6 0 0" :text="icon.heart" />
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
id="delete-action"
|
||||
col="2"
|
||||
class="swipe-item right"
|
||||
verticalAlignment="top"
|
||||
>
|
||||
<GridLayout columns="*, auto">
|
||||
<StackLayout id="delete-action" col="1" class="swipe-item right">
|
||||
<Label class="bx" padding="8 0 0 6" :text="icon.trash" />
|
||||
</StackLayout>
|
||||
</GridLayout>
|
||||
</v-template>
|
||||
<v-template name="footer">
|
||||
<StackLayout height="128"></StackLayout>
|
||||
</v-template>
|
||||
</RadListView>
|
||||
<Label
|
||||
v-if="!recipes.length && !filterFavorites && !filterMustTry"
|
||||
class="noResults"
|
||||
horizontalAlignment="center"
|
||||
text='Click the "+" icon to add a new recipe.'
|
||||
textAlignment="center"
|
||||
textWrap="true"
|
||||
/>
|
||||
<Label
|
||||
v-if="!filteredRecipes.length && searchQuery"
|
||||
class="noResults"
|
||||
|
@ -134,12 +111,15 @@
|
|||
textAlignment="center"
|
||||
textWrap="true"
|
||||
/>
|
||||
<GridLayout
|
||||
id="btnFabContainer"
|
||||
rows="*,88"
|
||||
columns="*,88"
|
||||
v-if="!showSearch"
|
||||
>
|
||||
<Label
|
||||
v-if="!filteredRecipes.length && filterMustTry && !searchQuery"
|
||||
class="noResults"
|
||||
horizontalAlignment="center"
|
||||
text="Your Must-Try recipes will be listed here."
|
||||
textAlignment="center"
|
||||
textWrap="true"
|
||||
/>
|
||||
<GridLayout id="btnFabContainer" rows="*,88" columns="*,88">
|
||||
<Label
|
||||
row="1"
|
||||
col="1"
|
||||
|
@ -157,15 +137,25 @@
|
|||
import * as utils from "tns-core-modules/utils/utils"
|
||||
import * as application from "tns-core-modules/application"
|
||||
import * as gestures from "tns-core-modules/ui/gestures"
|
||||
import * as Toast from "nativescript-toast"
|
||||
|
||||
import { ObservableArray } from "tns-core-modules/data/observable-array"
|
||||
|
||||
import EditRecipe from "./EditRecipe.vue"
|
||||
import ViewRecipe from "./ViewRecipe.vue"
|
||||
import ActionDialog from "./modal/ActionDialog.vue"
|
||||
import ConfirmDialog from "./modal/ConfirmDialog.vue"
|
||||
import { mapState, mapActions } from "vuex"
|
||||
|
||||
export default {
|
||||
props: ["filterFavorites", "selectedCategory", "title", "showDrawer"],
|
||||
props: [
|
||||
"filterFavorites",
|
||||
"filterMustTry",
|
||||
"selectedCategory",
|
||||
"showDrawer",
|
||||
"hijackGlobalBackEvent",
|
||||
"releaseGlobalBackEvent",
|
||||
],
|
||||
components: {
|
||||
EditRecipe,
|
||||
ViewRecipe,
|
||||
|
@ -173,80 +163,182 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
searchQuery: "",
|
||||
showSearch: false,
|
||||
viewIsScrolled: false,
|
||||
leftAction: false,
|
||||
showSearch: false,
|
||||
// leftAction: false,
|
||||
rightAction: false,
|
||||
sortType: "Natural order",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["recipes", "icon"]),
|
||||
recipesByCategory() {
|
||||
return this.recipes.reduce((acc, e) => {
|
||||
acc[e.category] = [...(acc[e.category] || []), e]
|
||||
return acc
|
||||
}, {})
|
||||
},
|
||||
...mapState(["recipes", "icon", "currentComponent"]),
|
||||
filteredRecipes() {
|
||||
if (this.selectedCategory) {
|
||||
return this.recipesByCategory[this.selectedCategory].filter((e) => {
|
||||
if (e.title.toLowerCase().includes(this.searchQuery)) return e
|
||||
})
|
||||
} else if (this.filterFavorites) {
|
||||
console.log("fav")
|
||||
return this.recipes.filter((e) => {
|
||||
if (e.isFavorite) {
|
||||
if (e.title.toLowerCase().includes(this.searchQuery)) return e
|
||||
}
|
||||
})
|
||||
if (this.filterFavorites) {
|
||||
return this.recipes.filter(
|
||||
(e) =>
|
||||
e.isFavorite && e.title.toLowerCase().includes(this.searchQuery)
|
||||
)
|
||||
} else if (this.filterMustTry) {
|
||||
return this.recipes.filter(
|
||||
(e) => !e.tried && e.title.toLowerCase().includes(this.searchQuery)
|
||||
)
|
||||
} else if (this.selectedCategory) {
|
||||
return this.recipes.filter(
|
||||
(e) =>
|
||||
e.category === this.selectedCategory &&
|
||||
e.title.toLowerCase().includes(this.searchQuery)
|
||||
)
|
||||
} else {
|
||||
return this.recipes.filter((e) => {
|
||||
if (e.title.toLowerCase().includes(this.searchQuery)) return e
|
||||
})
|
||||
return this.recipes.filter((e) =>
|
||||
e.title.toLowerCase().includes(this.searchQuery)
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openSearch() {
|
||||
this.showSearch = true
|
||||
this.hijackLocalBackEvent()
|
||||
},
|
||||
hijackLocalBackEvent() {
|
||||
this.releaseGlobalBackEvent()
|
||||
application.android.on(
|
||||
application.AndroidApplication.activityBackPressedEvent,
|
||||
this.searchBackEvent
|
||||
)
|
||||
},
|
||||
releaseLocalBackEvent() {
|
||||
application.android.off(
|
||||
application.AndroidApplication.activityBackPressedEvent,
|
||||
this.searchBackEvent
|
||||
)
|
||||
this.hijackGlobalBackEvent()
|
||||
},
|
||||
searchBackEvent(args) {
|
||||
args.cancel = true
|
||||
this.closeSearch()
|
||||
},
|
||||
closeSearch() {
|
||||
this.searchQuery = ""
|
||||
utils.ad.dismissSoftInput()
|
||||
this.showSearch = false
|
||||
this.updateFilter()
|
||||
this.releaseLocalBackEvent()
|
||||
},
|
||||
sortDialog() {
|
||||
this.releaseGlobalBackEvent()
|
||||
this.$showModal(ActionDialog, {
|
||||
props: {
|
||||
title: "Sort by",
|
||||
list: ["Natural order", "Title", "Duration", "Last modified"],
|
||||
height: "auto",
|
||||
},
|
||||
}).then((action) => {
|
||||
if (action && action !== "Cancel" && this.sortType !== action) {
|
||||
this.sortType = action
|
||||
this.updateSort()
|
||||
}
|
||||
this.hijackGlobalBackEvent()
|
||||
})
|
||||
},
|
||||
updateSort() {
|
||||
let listView = this.$refs.listView.nativeView
|
||||
listView.sortingFunction = undefined
|
||||
listView.sortingFunction = this.sortFunction
|
||||
},
|
||||
sortFunction(item, otherItem) {
|
||||
const titleOrder = item.title
|
||||
.toLowerCase()
|
||||
.localeCompare(otherItem.title.toLowerCase(), "en", {
|
||||
ignorePunctuation: true,
|
||||
})
|
||||
let d1 = this.recipeDuration(item.prepTime, item.cookTime)
|
||||
let d2 = this.recipeDuration(otherItem.prepTime, otherItem.cookTime)
|
||||
let ld1 = new Date(item.lastModified)
|
||||
let ld2 = new Date(otherItem.lastModified)
|
||||
switch (this.sortType) {
|
||||
case "Title":
|
||||
return titleOrder > 0 ? -1 : titleOrder < 0 ? 1 : 0
|
||||
break
|
||||
case "Duration":
|
||||
return d1 > d2 ? -1 : d1 < d2 ? 1 : 0
|
||||
break
|
||||
case "Last modified":
|
||||
return ld1 < ld2 ? -1 : ld1 > ld2 ? 1 : 0
|
||||
break
|
||||
default:
|
||||
return 0
|
||||
break
|
||||
}
|
||||
},
|
||||
setComponent(comp) {
|
||||
this.$store.dispatch("setCurrentComponent", comp)
|
||||
this.hijackGlobalBackEvent()
|
||||
},
|
||||
updateFilter() {
|
||||
let listView = this.$refs.listView.nativeView
|
||||
setTimeout((e) => {
|
||||
listView.filteringFunction = undefined
|
||||
listView.filteringFunction = this.filterFunction
|
||||
}, 1)
|
||||
},
|
||||
filterFunction(item) {
|
||||
if (this.filterFavorites) {
|
||||
return item.isFavorite
|
||||
? item.title.toLowerCase().includes(this.searchQuery)
|
||||
: false
|
||||
} else if (this.filterMustTry) {
|
||||
return item.tried
|
||||
? false
|
||||
: item.title.toLowerCase().includes(this.searchQuery)
|
||||
} else if (this.selectedCategory) {
|
||||
return item.category === this.selectedCategory
|
||||
? item.title.toLowerCase().includes(this.searchQuery)
|
||||
: false
|
||||
} else {
|
||||
return item.title.toLowerCase().includes(this.searchQuery)
|
||||
}
|
||||
},
|
||||
|
||||
setCurrentComponent() {
|
||||
this.filterFavorites
|
||||
? this.setComponent("Favorites")
|
||||
: this.filterMustTry
|
||||
? this.setComponent("Must-Try")
|
||||
: this.selectedCategory
|
||||
? this.setComponent(this.selectedCategory)
|
||||
: this.setComponent("EnRecipes")
|
||||
},
|
||||
onSwiping({ data, object }) {
|
||||
const swipeLimits = data.swipeLimits
|
||||
const swipeView = object
|
||||
const leftItem = swipeView.getViewById("favorite-action")
|
||||
const rightItem = swipeView.getViewById("delete-action")
|
||||
swipeLimits.left = leftItem.getMeasuredWidth() - 12
|
||||
swipeLimits.right = rightItem.getMeasuredWidth() - 12
|
||||
swipeLimits.threshold = swipeLimits.left - 4
|
||||
if (data.x > swipeLimits.threshold) {
|
||||
this.leftAction = true
|
||||
this.$refs.listView.notifySwipeToExecuteFinished()
|
||||
} else if (data.x < -swipeLimits.threshold) {
|
||||
swipeLimits.threshold = swipeLimits.right - 6
|
||||
if (data.x < -swipeLimits.threshold) {
|
||||
this.rightAction = true
|
||||
this.$refs.listView.notifySwipeToExecuteFinished()
|
||||
swipeView.notifySwipeToExecuteFinished()
|
||||
}
|
||||
},
|
||||
onSwipeEnded({ index }) {
|
||||
let context = this.recipes.indexOf(this.filteredRecipes[index])
|
||||
if (this.leftAction) this.toggleFavorite(context)
|
||||
else if (this.rightAction) this.deleteRecipe()
|
||||
this.leftAction = this.rightAction = false
|
||||
if (this.rightAction) this.deleteRecipe(index)
|
||||
this.rightAction = false
|
||||
},
|
||||
toggleFavorite(index) {
|
||||
this.$store.dispatch("toggleFavorite", index)
|
||||
deleteRecipe(index) {
|
||||
this.$showModal(ConfirmDialog, {
|
||||
props: {
|
||||
title: "Delete recipe",
|
||||
description: `Are you sure you want to delete the recipe "${this.recipes[index].title}"?`,
|
||||
cancelButtonText: "CANCEL",
|
||||
okButtonText: "DELETE",
|
||||
},
|
||||
deleteRecipe() {
|
||||
alert("Are you sure you want to delete?")
|
||||
}).then((action) => {
|
||||
if (action) {
|
||||
this.$store.dispatch("deleteRecipe", index)
|
||||
}
|
||||
})
|
||||
},
|
||||
// swipeAction(args, index) {
|
||||
// let vm = this
|
||||
// args.object.on(gestures.GestureTypes.swipe, function(args) {
|
||||
// console.log("Swipe Direction: " + args.direction)
|
||||
// if (args.direction === 1) {
|
||||
// vm.filteredRecipes[index].isFavorite = !vm.filteredRecipes[index]
|
||||
// .isFavorite
|
||||
// console.log(vm.filteredRecipes[index].isFavorite)
|
||||
// }
|
||||
// })
|
||||
// },
|
||||
recipeTotalTime(prepTime, cookTime) {
|
||||
getTotalTime(prepTime, cookTime) {
|
||||
let pT = prepTime.split(":")
|
||||
let cT = cookTime.split(":")
|
||||
let hrs = parseInt(pT[0]) + parseInt(cT[0])
|
||||
|
@ -255,42 +347,26 @@ export default {
|
|||
hrs += Math.floor(mins / 60)
|
||||
mins -= 60
|
||||
}
|
||||
return {
|
||||
hrs,
|
||||
mins,
|
||||
}
|
||||
},
|
||||
recipeTotalTime(prepTime, cookTime) {
|
||||
let { hrs, mins } = this.getTotalTime(prepTime, cookTime)
|
||||
return hrs ? `${hrs}h ${mins}m` : `${mins}m`
|
||||
},
|
||||
recipeDuration(prepTime, cookTime) {
|
||||
let { hrs, mins } = this.getTotalTime(prepTime, cookTime)
|
||||
return `${hrs}${mins}`
|
||||
},
|
||||
onScroll(args) {
|
||||
args.scrollOffset
|
||||
? (this.viewIsScrolled = true)
|
||||
: (this.viewIsScrolled = false)
|
||||
},
|
||||
// SearchBar
|
||||
searchBarLoaded() {
|
||||
application.android.on(
|
||||
application.AndroidApplication.activityBackPressedEvent,
|
||||
this.backEvent
|
||||
)
|
||||
},
|
||||
backEvent(args) {
|
||||
if (this.showSearch) {
|
||||
args.cancel = true
|
||||
this.closeSearch()
|
||||
}
|
||||
this.removeBackEvent()
|
||||
},
|
||||
removeBackEvent() {
|
||||
application.android.off(
|
||||
application.AndroidApplication.activityBackPressedEvent,
|
||||
this.backEvent
|
||||
)
|
||||
},
|
||||
closeSearch() {
|
||||
this.searchQuery = ""
|
||||
this.showSearch = false
|
||||
utils.ad.dismissSoftInput()
|
||||
},
|
||||
FabTapped() {
|
||||
alert("fab tapped")
|
||||
},
|
||||
addRecipe() {
|
||||
this.releaseGlobalBackEvent()
|
||||
this.$navigateTo(EditRecipe, {
|
||||
transition: {
|
||||
name: "slide",
|
||||
|
@ -299,11 +375,11 @@ export default {
|
|||
},
|
||||
props: {
|
||||
viewIsScrolled: this.viewIsScrolled,
|
||||
selectedCategory: this.selectedCategory,
|
||||
},
|
||||
})
|
||||
},
|
||||
viewRecipe({ item }) {
|
||||
// console.log(item)
|
||||
this.$navigateTo(ViewRecipe, {
|
||||
transition: {
|
||||
name: "fade",
|
||||
|
@ -311,7 +387,9 @@ export default {
|
|||
curve: "easeIn",
|
||||
},
|
||||
props: {
|
||||
recipe: item,
|
||||
recipeIndex: this.recipes.indexOf(item),
|
||||
hijackGlobalBackEvent: this.hijackGlobalBackEvent,
|
||||
releaseGlobalBackEvent: this.releaseGlobalBackEvent,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Page>
|
||||
<Page @loaded="setCurrentComponent">
|
||||
<ActionBar :flat="viewIsScrolled ? false : true">
|
||||
<!-- Settings Actionbar -->
|
||||
<GridLayout rows="*" columns="auto, *" class="actionBarContainer">
|
||||
|
@ -10,7 +10,7 @@
|
|||
@tap="showDrawer"
|
||||
col="0"
|
||||
/>
|
||||
<Label class="title orkm" :text="title" col="1" />
|
||||
<Label class="title orkm" text="Settings" col="1" />
|
||||
</GridLayout>
|
||||
</ActionBar>
|
||||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
|
@ -22,6 +22,7 @@
|
|||
class="option"
|
||||
@tap="selectThemes"
|
||||
>
|
||||
<!-- @tap="selectThemes" -->
|
||||
<Label verticalAlignment="center" class="bx" :text="icon.theme" />
|
||||
<StackLayout>
|
||||
<Label text="Theme" class="option-title" />
|
||||
|
@ -57,16 +58,24 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Menu } from "nativescript-menu"
|
||||
import * as permissions from "nativescript-permissions"
|
||||
import * as application from "tns-core-modules/application"
|
||||
|
||||
import { getString, setString } from "application-settings"
|
||||
import Theme from "@nativescript/theme"
|
||||
import ActionDialog from "./modal/ActionDialog.vue"
|
||||
import ConfirmDialog from "./modal/ConfirmDialog.vue"
|
||||
|
||||
import { mapState, mapActions } from "vuex"
|
||||
export default {
|
||||
props: ["highlight", "viewIsScrolled", "showDrawer", "title"],
|
||||
props: [
|
||||
"highlight",
|
||||
"viewIsScrolled",
|
||||
"showDrawer",
|
||||
"restartApp",
|
||||
"hijackGlobalBackEvent",
|
||||
"releaseGlobalBackEvent",
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
interface: {
|
||||
|
@ -98,23 +107,44 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["icon"]),
|
||||
...mapState(["icon", "currentComponent"]),
|
||||
},
|
||||
methods: {
|
||||
setCurrentComponent() {
|
||||
this.$store.dispatch("setCurrentComponent", "Settings")
|
||||
this.releaseGlobalBackEvent()
|
||||
},
|
||||
showDialog(args) {
|
||||
this.highlight(args)
|
||||
this.$showModal(ActionDialog)
|
||||
},
|
||||
selectThemes(args) {
|
||||
this.highlight(args)
|
||||
let btn = args.object
|
||||
Menu.popup({
|
||||
view: btn,
|
||||
actions: this.themesArray,
|
||||
this.$showModal(ActionDialog, {
|
||||
props: {
|
||||
title: "Theme",
|
||||
list: ["Light", "Dark"],
|
||||
height: "96",
|
||||
},
|
||||
}).then((action) => {
|
||||
if (action && action !== "Cancel" && this.themeName !== action) {
|
||||
this.$showModal(ConfirmDialog, {
|
||||
props: {
|
||||
title: "App Reload Required",
|
||||
description:
|
||||
"The app needs to be reloaded for the theme change to take effect.",
|
||||
cancelButtonText: "CANCEL",
|
||||
okButtonText: "RELOAD",
|
||||
},
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
this.interface.theme.subTitle = this.themeName = action
|
||||
setString("application-theme", action)
|
||||
setTimeout((e) => this.restartApp(), 250)
|
||||
}
|
||||
})
|
||||
.then((action) => {
|
||||
this.interface.theme.subTitle = this.themeName = action.title
|
||||
console.log(this.themeName)
|
||||
setString("application-theme", action.title)
|
||||
Theme.toggleMode()
|
||||
}
|
||||
})
|
||||
.catch(console.log)
|
||||
},
|
||||
selectBackupDir(args) {
|
||||
this.highlight(args)
|
||||
|
|
|
@ -1,41 +1,48 @@
|
|||
<template>
|
||||
<Page>
|
||||
<ActionBar margin="0" flat="true">
|
||||
<Page @loaded="setCurrentComponent">
|
||||
<ActionBar height="128" margin="0" flat="true">
|
||||
<GridLayout
|
||||
rows="*"
|
||||
columns="auto, *, auto,auto"
|
||||
rows="64, 64"
|
||||
columns="auto, *, auto,auto, auto"
|
||||
class="actionBarContainer"
|
||||
>
|
||||
<Label
|
||||
row="0"
|
||||
col="0"
|
||||
class="bx leftAction"
|
||||
:text="icon.back"
|
||||
automationText="Back"
|
||||
@tap="navigateBack"
|
||||
verticalAlignment="top"
|
||||
@tap="$navigateBack()"
|
||||
/>
|
||||
<ScrollView
|
||||
row="1"
|
||||
col="1"
|
||||
colSpan="3"
|
||||
orientation="horizontal"
|
||||
scrollBarIndicatorVisible="false"
|
||||
>
|
||||
<Label
|
||||
class="title orkm"
|
||||
:text="recipe.title"
|
||||
lineHeight="4"
|
||||
col="1"
|
||||
textWrap="true"
|
||||
verticalAlignment="bottom"
|
||||
/>
|
||||
</ScrollView>
|
||||
<Label row="0" col="2" class="bx" :text="icon.share" @tap="" />
|
||||
<Label
|
||||
col="2"
|
||||
class="bx"
|
||||
:text="icon.share"
|
||||
@tap=""
|
||||
verticalAlignment="top"
|
||||
/>
|
||||
<Label
|
||||
row="0"
|
||||
col="3"
|
||||
class="bx"
|
||||
:class="{ 'view-favorited': recipe.isFavorite }"
|
||||
:text="recipe.isFavorite ? icon.heart : icon.heartOutline"
|
||||
@tap="toggleFavorite"
|
||||
verticalAlignment="top"
|
||||
/>
|
||||
<Label
|
||||
row="0"
|
||||
col="4"
|
||||
class="bx"
|
||||
:class="{ 'view-favorited': !recipe.tried }"
|
||||
:text="recipe.tried ? icon.musttryOutline : icon.musttry"
|
||||
@tap="toggleMustTry"
|
||||
/>
|
||||
</GridLayout>
|
||||
</ActionBar>
|
||||
|
@ -43,7 +50,7 @@
|
|||
<TabView androidElevation="0" width="100%" height="100%">
|
||||
<TabViewItem title="Overview">
|
||||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
<StackLayout class="">
|
||||
<StackLayout>
|
||||
<StackLayout
|
||||
width="100%"
|
||||
:height="screenWidth"
|
||||
|
@ -62,13 +69,13 @@
|
|||
horizontalAlignment="center"
|
||||
class="bx"
|
||||
fontSize="160"
|
||||
:text="icon.dish"
|
||||
:text="icon.image"
|
||||
/>
|
||||
</StackLayout>
|
||||
<StackLayout margin="16 16 128">
|
||||
<StackLayout margin="16 16 144">
|
||||
<Label class="view-cat orkm" :text="recipe.category" />
|
||||
<Label
|
||||
class="view-title p-b-8 orkm"
|
||||
class="view-title orkm"
|
||||
:text="recipe.title"
|
||||
textWrap="true"
|
||||
/>
|
||||
|
@ -88,9 +95,9 @@
|
|||
</StackLayout>
|
||||
</ScrollView>
|
||||
</TabViewItem>
|
||||
<TabViewItem title="Ingredients" v-if="recipe.ingredients[0].item">
|
||||
<TabViewItem title="Ingredients" v-if="recipe.ingredients.length">
|
||||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
<StackLayout padding="16 16 128">
|
||||
<StackLayout padding="16 16 124">
|
||||
<AbsoluteLayout class="inputField">
|
||||
<TextField
|
||||
width="50%"
|
||||
|
@ -101,7 +108,7 @@
|
|||
</AbsoluteLayout>
|
||||
<StackLayout margin="24 0 8 0">
|
||||
<Label
|
||||
class="view-title p-b-8 orkm"
|
||||
class="view-title orkm"
|
||||
:text="
|
||||
`Ingredients for ${portionScale}${
|
||||
portionScale > 1
|
||||
|
@ -128,9 +135,9 @@
|
|||
</StackLayout>
|
||||
</ScrollView>
|
||||
</TabViewItem>
|
||||
<TabViewItem title="Instructions" v-if="recipe.instructions[0].length">
|
||||
<TabViewItem title="Instructions" v-if="recipe.instructions.length">
|
||||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
<StackLayout padding="16 16 128">
|
||||
<StackLayout padding="32 16 132">
|
||||
<GridLayout
|
||||
columns="auto ,*"
|
||||
v-for="(instruction, index) in recipe.instructions"
|
||||
|
@ -158,9 +165,9 @@
|
|||
</StackLayout>
|
||||
</ScrollView>
|
||||
</TabViewItem>
|
||||
<TabViewItem title="Notes" v-if="recipe.notes[0].length">
|
||||
<TabViewItem title="Notes" v-if="recipe.notes.length">
|
||||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
<StackLayout padding="16 16 128">
|
||||
<StackLayout padding="32 16 132">
|
||||
<GridLayout
|
||||
columns="auto ,*"
|
||||
v-for="(note, index) in recipe.notes"
|
||||
|
@ -184,9 +191,9 @@
|
|||
</StackLayout>
|
||||
</ScrollView>
|
||||
</TabViewItem>
|
||||
<TabViewItem title="References" v-if="recipe.references[0].length">
|
||||
<TabViewItem title="References" v-if="recipe.references.length">
|
||||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
<StackLayout padding="16 16 128">
|
||||
<StackLayout padding="32 16 132">
|
||||
<GridLayout
|
||||
columns="auto ,*"
|
||||
v-for="(reference, index) in recipe.references"
|
||||
|
@ -214,6 +221,7 @@
|
|||
</TabView>
|
||||
<GridLayout id="btnFabContainer" rows="*,88" columns="*,88">
|
||||
<Label
|
||||
v-if="!busy"
|
||||
row="1"
|
||||
col="1"
|
||||
class="bx btnFab"
|
||||
|
@ -221,6 +229,7 @@
|
|||
androidElevation="8"
|
||||
@tap="editRecipe"
|
||||
/>
|
||||
<ActivityIndicator v-else row="1" col="1" :busy="busy" />
|
||||
</GridLayout>
|
||||
</AbsoluteLayout>
|
||||
</Page>
|
||||
|
@ -229,18 +238,26 @@
|
|||
<script>
|
||||
import { screen } from "tns-core-modules/platform"
|
||||
import * as utils from "tns-core-modules/utils/utils"
|
||||
import { getNumber, setNumber } from "application-settings"
|
||||
import * as Toast from "nativescript-toast"
|
||||
|
||||
import EditRecipe from "./EditRecipe.vue"
|
||||
|
||||
import { mapState, mapActions } from "vuex"
|
||||
|
||||
export default {
|
||||
props: ["recipe"],
|
||||
props: ["recipeIndex", "hijackGlobalBackEvent", "releaseGlobalBackEvent"],
|
||||
data() {
|
||||
return {
|
||||
busy: false,
|
||||
portionScale: 1,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["icon", "recipes"]),
|
||||
recipe() {
|
||||
return this.recipes[this.recipeIndex]
|
||||
},
|
||||
screenWidth() {
|
||||
return screen.mainScreen.widthDIPs
|
||||
},
|
||||
|
@ -254,18 +271,33 @@ export default {
|
|||
roundedQuantity(quantity, unit) {
|
||||
return Math.round(quantity * this.isPortionScalePositive * 100) / 100
|
||||
},
|
||||
// indexChange(args) {
|
||||
// let newIndex = args.value
|
||||
// console.log("Current tab index: " + newIndex)
|
||||
// },
|
||||
navigateBack() {
|
||||
this.$navigateBack()
|
||||
},
|
||||
editRecipe() {
|
||||
alert("edit recipe")
|
||||
this.busy = true
|
||||
this.$navigateTo(EditRecipe, {
|
||||
transition: {
|
||||
name: "slide",
|
||||
duration: 250,
|
||||
curve: "easeIn",
|
||||
},
|
||||
props: {
|
||||
recipeIndex: this.recipeIndex,
|
||||
},
|
||||
// backstackVisible: false,
|
||||
})
|
||||
},
|
||||
toggleFavorite() {
|
||||
this.$store.dispatch("toggleFavorite", this.recipes.indexOf(this.recipe))
|
||||
this.recipe.isFavorite
|
||||
? Toast.makeText("Removed from Favorites").show()
|
||||
: Toast.makeText("Added to Favorites").show()
|
||||
|
||||
this.$store.dispatch("toggleFavorite", this.recipeIndex)
|
||||
},
|
||||
toggleMustTry() {
|
||||
this.recipe.tried
|
||||
? Toast.makeText("Added to Must-Try").show()
|
||||
: Toast.makeText("Removed from Must-Try").show()
|
||||
|
||||
this.$store.dispatch("toggleMustTry", this.recipeIndex)
|
||||
},
|
||||
getTime(time) {
|
||||
let t = time.split(":")
|
||||
|
@ -276,14 +308,13 @@ export default {
|
|||
openURL(args, url) {
|
||||
utils.openUrl(url)
|
||||
},
|
||||
setCurrentComponent() {
|
||||
this.releaseGlobalBackEvent()
|
||||
this.busy = false
|
||||
setTimeout((e) => {
|
||||
this.$store.dispatch("setCurrentComponent", "ViewRecipe")
|
||||
}, 500)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
ActionBar {
|
||||
height: 128;
|
||||
}
|
||||
.actionBarContainer .bx {
|
||||
margin-top: 4;
|
||||
}
|
||||
</style>
|
||||
|
|
64
app/components/modal/ActionDialog.vue
Normal file
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<Page>
|
||||
<StackLayout class="dialogContainer">
|
||||
<Label class="dialogTitle orkm" :text="title" />
|
||||
<StackLayout class="actionsContainer">
|
||||
<ListView
|
||||
width="100%"
|
||||
:height="height"
|
||||
for="item in list"
|
||||
@itemTap="tapAction"
|
||||
separatorColor="transparent"
|
||||
>
|
||||
<v-template>
|
||||
<StackLayout class="actionItem">
|
||||
<Label :text="item" />
|
||||
</StackLayout>
|
||||
</v-template>
|
||||
</ListView>
|
||||
</StackLayout>
|
||||
<GridLayout rows="auto" columns="auto, *, auto">
|
||||
<Label
|
||||
v-if="action"
|
||||
col="0"
|
||||
class="cancel orkm pull-left"
|
||||
:text="action"
|
||||
@tap="$modal.close(action)"
|
||||
/>
|
||||
<Label
|
||||
col="2"
|
||||
class="cancel orkm pull-right"
|
||||
text="CANCEL"
|
||||
@tap="$modal.close(false)"
|
||||
/>
|
||||
</GridLayout>
|
||||
</StackLayout>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ["title", "list", "height", "action"],
|
||||
methods: {
|
||||
tapAction({ item }) {
|
||||
this.$modal.close(item)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dialogTitle {
|
||||
padding: 24 24 12;
|
||||
font-size: 20;
|
||||
}
|
||||
.actionItem {
|
||||
width: 100%;
|
||||
font-size: 16;
|
||||
padding: 8 20;
|
||||
}
|
||||
.cancel {
|
||||
padding: 24;
|
||||
font-size: 12;
|
||||
color: #ff7043;
|
||||
}
|
||||
</style>
|
45
app/components/modal/ConfirmDialog.vue
Normal file
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<Page>
|
||||
<StackLayout class="dialogContainer">
|
||||
<Label class="dialogTitle orkm" :text="title" />
|
||||
<Label class="dialogDescription" :text="description" textWrap="true" />
|
||||
<StackLayout
|
||||
orientation="horizontal"
|
||||
class="actionsContainer"
|
||||
horizontalAlignment="right"
|
||||
>
|
||||
<Label
|
||||
class="action orkm pull-right"
|
||||
:text="cancelButtonText"
|
||||
@tap="$modal.close(false)"
|
||||
/>
|
||||
<Label
|
||||
class="action orkm pull-right"
|
||||
:text="okButtonText"
|
||||
@tap="$modal.close(true)"
|
||||
/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ["title", "description", "cancelButtonText", "okButtonText"],
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dialogTitle {
|
||||
padding: 24 24 12;
|
||||
font-size: 20;
|
||||
}
|
||||
.dialogDescription {
|
||||
font-size: 16;
|
||||
padding: 0 24 16;
|
||||
}
|
||||
.action {
|
||||
padding: 24 24 24 8;
|
||||
font-size: 12;
|
||||
color: #ff7043;
|
||||
}
|
||||
</style>
|
49
app/components/modal/PromptDialog.vue
Normal file
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<Page>
|
||||
<StackLayout class="dialogContainer">
|
||||
<Label class="dialogTitle orkm" :text="title" />
|
||||
<TextField
|
||||
width="100%"
|
||||
:hint="hint"
|
||||
v-model="category"
|
||||
autocapitalizationType="words"
|
||||
/>
|
||||
<StackLayout orientation="horizontal" horizontalAlignment="right">
|
||||
<Label class="action orkm" text="CANCEL" @tap="$modal.close(false)" />
|
||||
<Label
|
||||
class="action orkm"
|
||||
:text="action"
|
||||
@tap="$modal.close(category)"
|
||||
/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ["title", "hint", "action"],
|
||||
data() {
|
||||
return {
|
||||
category: null,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
TextField {
|
||||
margin: 0 24 16;
|
||||
}
|
||||
.dialogContainer {
|
||||
padding: 0 24;
|
||||
}
|
||||
.dialogTitle {
|
||||
padding: 24 0 12;
|
||||
font-size: 20;
|
||||
}
|
||||
.action {
|
||||
padding: 24 0 24 32;
|
||||
font-size: 12;
|
||||
color: #ff7043;
|
||||
}
|
||||
</style>
|
54
app/main.js
|
@ -1,55 +1,25 @@
|
|||
// import VueDevtools from "nativescript-vue-devtools"
|
||||
import Vue from "nativescript-vue"
|
||||
import App from "./components/App"
|
||||
import RadListView from "nativescript-ui-listview/vue"
|
||||
|
||||
|
||||
// Vue.registerElement(
|
||||
// "RadListView",
|
||||
// () => require("nativescript-ui-listview/vue").RadListView
|
||||
// )
|
||||
Vue.use(RadListView)
|
||||
|
||||
// Vue.use(VueDevtools)
|
||||
|
||||
// Vue.registerElement(
|
||||
// "CheckBox",
|
||||
// () => require("@nstudio/nativescript-checkbox").CheckBox,
|
||||
// {
|
||||
// model: {
|
||||
// prop: "checked",
|
||||
// event: "checkedChange",
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
import DateTimePicker from "nativescript-datetimepicker/vue"
|
||||
Vue.use(DateTimePicker)
|
||||
// import VueDevtools from 'nativescript-vue-devtools'
|
||||
|
||||
// import { TNSFontIcon, fonticon } from 'nativescript-fonticon'
|
||||
|
||||
// TNSFontIcon.debug = true
|
||||
// TNSFontIcon.paths = {
|
||||
// // bx: './assets/boxicons.css',
|
||||
// fa: './assets/fontawesome.css',
|
||||
// }
|
||||
// TNSFontIcon.loadCss()
|
||||
|
||||
// Vue.filter('fonticon', fonticon)
|
||||
|
||||
if (TNS_ENV !== "production") {
|
||||
// Vue.use(VueDevtools)
|
||||
}
|
||||
import store from "./store"
|
||||
|
||||
// Prints Vue logs when --env.production is *NOT* set while building
|
||||
Vue.config.silent = TNS_ENV === "production"
|
||||
import RadListView from "nativescript-ui-listview/vue"
|
||||
Vue.use(RadListView)
|
||||
|
||||
import DateTimePicker from "nativescript-datetimepicker/vue"
|
||||
Vue.use(DateTimePicker)
|
||||
|
||||
Vue.registerElement(
|
||||
"RadSideDrawer",
|
||||
() => require("nativescript-ui-sidedrawer").RadSideDrawer
|
||||
)
|
||||
|
||||
if (TNS_ENV !== "production") {
|
||||
// Vue.use(VueDevtools)
|
||||
}
|
||||
|
||||
// Prints Vue logs when --env.production is *NOT* set while building
|
||||
Vue.config.silent = TNS_ENV === "production"
|
||||
|
||||
new Vue({
|
||||
store,
|
||||
render: (h) => h("frame", [h(App)]),
|
||||
|
|
461
app/store.js
|
@ -6,188 +6,178 @@ Vue.use(Vuex)
|
|||
export default new Vuex.Store({
|
||||
state: {
|
||||
recipes: [
|
||||
{
|
||||
imageSrc: null,
|
||||
title: "Mediterranean Salad",
|
||||
category: "Salads",
|
||||
prepTime: "00:10",
|
||||
cookTime: "00:20",
|
||||
portionSize: 1,
|
||||
ingredients: [
|
||||
{
|
||||
item: "Cucumbers, Seeded And Sliced",
|
||||
quantity: 3,
|
||||
unit: null,
|
||||
},
|
||||
{
|
||||
item: "Crumbled Feta Cheese",
|
||||
quantity: 1.5,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Black Olives, Pitted And Sliced",
|
||||
quantity: 1,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Diced Roma Tomatoes",
|
||||
quantity: 3,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Diced Oil Packed Sun Dried Tomatoes, Drained, Oil Reserved",
|
||||
quantity: 0.3,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Onion, Sliced",
|
||||
quantity: 1.5,
|
||||
unit: null,
|
||||
},
|
||||
{
|
||||
item: "Cucumbers, Seeded And Sliced",
|
||||
quantity: 3,
|
||||
unit: null,
|
||||
},
|
||||
{
|
||||
item: "Crumbled Feta Cheese",
|
||||
quantity: 1.5,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Black Olives, Pitted And Sliced",
|
||||
quantity: 1,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Diced Roma Tomatoes",
|
||||
quantity: 3,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Diced Oil Packed Sun Dried Tomatoes, Drained, Oil Reserved",
|
||||
quantity: 0.3,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Onion, Sliced",
|
||||
quantity: 1.5,
|
||||
unit: null,
|
||||
},
|
||||
{
|
||||
item: "Cucumbers, Seeded And Sliced",
|
||||
quantity: 3,
|
||||
unit: null,
|
||||
},
|
||||
{
|
||||
item: "Crumbled Feta Cheese",
|
||||
quantity: 1.5,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Black Olives, Pitted And Sliced",
|
||||
quantity: 1,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Diced Roma Tomatoes",
|
||||
quantity: 3,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Diced Oil Packed Sun Dried Tomatoes, Drained, Oil Reserved",
|
||||
quantity: 0.3,
|
||||
unit: "cup",
|
||||
},
|
||||
{
|
||||
item: "Onion, Sliced",
|
||||
quantity: 1.5,
|
||||
unit: null,
|
||||
},
|
||||
],
|
||||
instructions: [
|
||||
"In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion.",
|
||||
"Chill until serving.",
|
||||
"In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion. In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion.",
|
||||
"Chill until serving.",
|
||||
"Chill until serving.",
|
||||
"In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion.",
|
||||
"Chill until serving.",
|
||||
"Chill until serving.",
|
||||
"Chill until serving.",
|
||||
"Chill until serving.",
|
||||
"Chill until serving.",
|
||||
],
|
||||
notes: [
|
||||
"Per Serving: 130.6 calories; protein 5.5g 11% DV; carbohydrates 9.3g 3% DV; fat 8.8g 14% DV; cholesterol 25mg 8% DV; sodium 486.4mg 20% DV.",
|
||||
"Per Serving: 130.6 calories; protein 5.5g 11% DV; carbohydrates 9.3g 3% DV; fat 8.8g 14% DV; cholesterol 25mg 8% DV; sodium 486.4mg 20% DV.",
|
||||
"Per Serving: 130.6 calories; protein 5.5g 11% DV; carbohydrates 9.3g 3% DV; fat 8.8g 14% DV; cholesterol 25mg 8% DV; sodium 486.4mg 20% DV.",
|
||||
],
|
||||
references: [
|
||||
"https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
|
||||
"https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
|
||||
"https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
|
||||
"https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
|
||||
],
|
||||
isFavorite: true,
|
||||
},
|
||||
{
|
||||
imageSrc: null,
|
||||
title: "Fresh Tomato Sauce",
|
||||
category: "Sauces",
|
||||
prepTime: "00:20",
|
||||
cookTime: "00:25",
|
||||
portionSize: 1,
|
||||
ingredients: [
|
||||
{
|
||||
item: null,
|
||||
quantity: null,
|
||||
unit: null,
|
||||
},
|
||||
],
|
||||
instructions: [""],
|
||||
notes: [""],
|
||||
references: [""],
|
||||
isFavorite: true,
|
||||
},
|
||||
{
|
||||
imageSrc: null,
|
||||
title: "Creamy Mushroom Herb Pasta",
|
||||
category: "Lunch",
|
||||
prepTime: "00:45",
|
||||
cookTime: "00:25",
|
||||
portionSize: 1,
|
||||
ingredients: [
|
||||
{
|
||||
item: null,
|
||||
quantity: null,
|
||||
unit: null,
|
||||
},
|
||||
],
|
||||
instructions: [""],
|
||||
notes: [""],
|
||||
references: [""],
|
||||
isFavorite: false,
|
||||
},
|
||||
{
|
||||
imageSrc: null,
|
||||
title: "Grilled Cheese Sandwich",
|
||||
category: "Lunch",
|
||||
prepTime: "00:20",
|
||||
cookTime: "00:10",
|
||||
portionSize: 1,
|
||||
ingredients: [
|
||||
{
|
||||
item: null,
|
||||
quantity: null,
|
||||
unit: null,
|
||||
},
|
||||
],
|
||||
instructions: [""],
|
||||
notes: [""],
|
||||
references: [""],
|
||||
isFavorite: false,
|
||||
},
|
||||
// {
|
||||
// imageSrc: null,
|
||||
// title: "Mediterranean Salad",
|
||||
// category: "Salads",
|
||||
// prepTime: "12:25",
|
||||
// cookTime: "00:30",
|
||||
// portionSize: 1,
|
||||
// ingredients: [
|
||||
// {
|
||||
// item: "Cucumbers, Seeded And Sliced",
|
||||
// quantity: 3,
|
||||
// unit: "unit",
|
||||
// },
|
||||
// {
|
||||
// item: "Crumbled Feta Cheese",
|
||||
// quantity: 1.5,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Black Olives, Pitted And Sliced",
|
||||
// quantity: 1,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Diced Roma Tomatoes",
|
||||
// quantity: 3,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Diced Oil Packed Sun Dried Tomatoes, Drained, Oil Reserved",
|
||||
// quantity: 0.3,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Onion, Sliced",
|
||||
// quantity: 1.5,
|
||||
// unit: "unit",
|
||||
// },
|
||||
// {
|
||||
// item: "Cucumbers, Seeded And Sliced",
|
||||
// quantity: 3,
|
||||
// unit: "unit",
|
||||
// },
|
||||
// {
|
||||
// item: "Crumbled Feta Cheese",
|
||||
// quantity: 1.5,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Black Olives, Pitted And Sliced",
|
||||
// quantity: 1,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Diced Roma Tomatoes",
|
||||
// quantity: 3,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Diced Oil Packed Sun Dried Tomatoes, Drained, Oil Reserved",
|
||||
// quantity: 0.3,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Onion, Sliced",
|
||||
// quantity: 1.5,
|
||||
// unit: "unit",
|
||||
// },
|
||||
// {
|
||||
// item: "Cucumbers, Seeded And Sliced",
|
||||
// quantity: 3,
|
||||
// unit: "unit",
|
||||
// },
|
||||
// {
|
||||
// item: "Crumbled Feta Cheese",
|
||||
// quantity: 1.5,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Black Olives, Pitted And Sliced",
|
||||
// quantity: 1,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Diced Roma Tomatoes",
|
||||
// quantity: 3,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Diced Oil Packed Sun Dried Tomatoes, Drained, Oil Reserved",
|
||||
// quantity: 0.3,
|
||||
// unit: "cup",
|
||||
// },
|
||||
// {
|
||||
// item: "Onion, Sliced",
|
||||
// quantity: 1.5,
|
||||
// unit: "unit",
|
||||
// },
|
||||
// ],
|
||||
// instructions: [
|
||||
// "In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion.",
|
||||
// "Chill until serving.",
|
||||
// "In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion. In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion.",
|
||||
// "Chill until serving.",
|
||||
// "Chill until serving.",
|
||||
// "In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion.",
|
||||
// "Chill until serving.",
|
||||
// "Chill until serving.",
|
||||
// "Chill until serving.",
|
||||
// "Chill until serving.",
|
||||
// "Chill until serving.",
|
||||
// ],
|
||||
// notes: [
|
||||
// "Per Serving: 130.6 calories; protein 5.5g 11% DV; carbohydrates 9.3g 3% DV; fat 8.8g 14% DV; cholesterol 25mg 8% DV; sodium 486.4mg 20% DV.",
|
||||
// "Per Serving: 130.6 calories; protein 5.5g 11% DV; carbohydrates 9.3g 3% DV; fat 8.8g 14% DV; cholesterol 25mg 8% DV; sodium 486.4mg 20% DV.",
|
||||
// "Per Serving: 130.6 calories; protein 5.5g 11% DV; carbohydrates 9.3g 3% DV; fat 8.8g 14% DV; cholesterol 25mg 8% DV; sodium 486.4mg 20% DV.",
|
||||
// ],
|
||||
// references: [
|
||||
// "https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
|
||||
// "https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
|
||||
// "https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
|
||||
// "https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
|
||||
// ],
|
||||
// isFavorite: true,
|
||||
// tried: false,
|
||||
// lastModified: "2020-10-18T17:37:51.798Z",
|
||||
// },
|
||||
// {
|
||||
// imageSrc: null,
|
||||
// title: "Fresh Tomato Sauce",
|
||||
// category: "Sauces",
|
||||
// prepTime: "00:45",
|
||||
// cookTime: "00:35",
|
||||
// portionSize: 1,
|
||||
// ingredients: [],
|
||||
// instructions: [],
|
||||
// notes: [],
|
||||
// references: [],
|
||||
// isFavorite: true,
|
||||
// tried: true,
|
||||
// lastModified: "2020-10-15T17:37:51.798Z",
|
||||
// },
|
||||
// {
|
||||
// imageSrc: null,
|
||||
// title: "Creamy Mushroom Herb Pasta",
|
||||
// category: "Lunch",
|
||||
// prepTime: "00:10",
|
||||
// cookTime: "00:15",
|
||||
// portionSize: 1,
|
||||
// ingredients: [],
|
||||
// instructions: [],
|
||||
// notes: [],
|
||||
// references: [],
|
||||
// isFavorite: false,
|
||||
// tried: false,
|
||||
// lastModified: "2020-10-12T17:37:51.798Z",
|
||||
// },
|
||||
// {
|
||||
// imageSrc: null,
|
||||
// title: "Grilled Cheese Sandwich",
|
||||
// category: "Lunch",
|
||||
// prepTime: "00:50",
|
||||
// cookTime: "00:12",
|
||||
// portionSize: 1,
|
||||
// ingredients: [],
|
||||
// instructions: [],
|
||||
// notes: [],
|
||||
// references: [],
|
||||
// isFavorite: false,
|
||||
// tried: true,
|
||||
// lastModified: "2020-10-03T17:37:51.798Z",
|
||||
// },
|
||||
],
|
||||
viewIsScrolled: false,
|
||||
icon: {
|
||||
|
@ -202,7 +192,7 @@ export default new Vuex.Store({
|
|||
sort: "\ueb2b",
|
||||
plus: "\ueb89",
|
||||
close: "\uec4e",
|
||||
dish: "\uea71",
|
||||
image: "\ueae9",
|
||||
back: "\ue988",
|
||||
save: "\uee48",
|
||||
camera: "\uecc2",
|
||||
|
@ -216,22 +206,127 @@ export default new Vuex.Store({
|
|||
file: "\ued60",
|
||||
user: "\uee8e",
|
||||
trash: "\uee83",
|
||||
donate: "\ued41",
|
||||
musttry: "\uec96",
|
||||
musttryOutline: "\ue9bb",
|
||||
},
|
||||
units: [
|
||||
"unit",
|
||||
"tsp",
|
||||
"Tbsp",
|
||||
"oz",
|
||||
"cup",
|
||||
"pt",
|
||||
"qt",
|
||||
"lb",
|
||||
"gal",
|
||||
"ml",
|
||||
"L",
|
||||
"mg",
|
||||
"g",
|
||||
"kg",
|
||||
"mm",
|
||||
"cm",
|
||||
"m",
|
||||
"in",
|
||||
"°C",
|
||||
"°F",
|
||||
],
|
||||
categories: [
|
||||
"Appetizers",
|
||||
"BBQ",
|
||||
"Beverages",
|
||||
"Breads",
|
||||
"Breakfast",
|
||||
"Desserts",
|
||||
"Dinner",
|
||||
"Drinks",
|
||||
"Healthy",
|
||||
"Lunch",
|
||||
"Main dishes",
|
||||
"Meat",
|
||||
"Noodles",
|
||||
"Pasta",
|
||||
"Poultry",
|
||||
"Rice",
|
||||
"Salads",
|
||||
"Sauces",
|
||||
"Seafood",
|
||||
"Side dishes",
|
||||
"Snacks",
|
||||
"Soups",
|
||||
"Undefined",
|
||||
"Vegan",
|
||||
"Vegetarian",
|
||||
],
|
||||
currentComponent: "EnRecipes",
|
||||
},
|
||||
mutations: {
|
||||
addRecipe(state, recipe) {
|
||||
state.recipes.push(recipe)
|
||||
},
|
||||
addCategory(state, category) {
|
||||
let a = state.categories.filter((e) => e === category).length
|
||||
if (a == 0) {
|
||||
state.categories.push(category)
|
||||
state.categories.sort()
|
||||
}
|
||||
},
|
||||
overwriteRecipe(state, { index, recipe }) {
|
||||
Object.assign(state.recipes[index], recipe)
|
||||
},
|
||||
deleteRecipe(state, index) {
|
||||
state.recipes.splice(index, 1)
|
||||
},
|
||||
toggleFavorite(state, index) {
|
||||
state.recipes[index].isFavorite = !state.recipes[index].isFavorite
|
||||
},
|
||||
toggleMustTry(state, index) {
|
||||
state.recipes[index].tried = !state.recipes[index].tried
|
||||
},
|
||||
setCurrentComponent(state, comp) {
|
||||
state.currentComponent = comp
|
||||
},
|
||||
renameCategory(state, { current, updated }) {
|
||||
let a = state.categories.filter((e) => e === updated).length
|
||||
if (a == 0) {
|
||||
// add updated category to categories
|
||||
state.categories.splice(state.categories.indexOf(current), 1)
|
||||
state.categories.push(updated)
|
||||
state.categories.sort()
|
||||
// rename all occurences
|
||||
state.recipes.forEach((e, i) => {
|
||||
if (e.category == current) {
|
||||
state.recipes[i].category = updated
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
addRecipe({ commit }, recipe) {
|
||||
commit("addRecipe", recipe)
|
||||
},
|
||||
addCategory({ commit }, category) {
|
||||
commit("addCategory", category)
|
||||
},
|
||||
overwriteRecipe({ commit }, updatedRecipe) {
|
||||
commit("overwriteRecipe", updatedRecipe)
|
||||
},
|
||||
deleteRecipe({ commit }, index) {
|
||||
commit("deleteRecipe", index)
|
||||
},
|
||||
toggleFavorite({ commit }, index) {
|
||||
commit("toggleFavorite", index)
|
||||
},
|
||||
toggleMustTry({ commit }, index) {
|
||||
commit("toggleMustTry", index)
|
||||
},
|
||||
setCurrentComponent({ commit }, comp) {
|
||||
commit("setCurrentComponent", comp)
|
||||
},
|
||||
renameCategory({ commit }, category) {
|
||||
commit("renameCategory", category)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
1022
package-lock.json
generated
|
@ -19,20 +19,15 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@nativescript/theme": "^2.2.1",
|
||||
"@nstudio/nativescript-checkbox": "^1.0.0",
|
||||
"@vue/devtools": "^5.3.3",
|
||||
"nativescript-camera": "^4.5.0",
|
||||
"nativescript-couchbase-plugin": "^0.9.6",
|
||||
"nativescript-datetimepicker": "^1.2.3",
|
||||
"nativescript-fonticon": "^2.0.2",
|
||||
"nativescript-mediafilepicker": "^4.0.0",
|
||||
"nativescript-menu": "^1.1.6",
|
||||
"nativescript-permissions": "^1.3.9",
|
||||
"nativescript-socketio": "^3.3.1",
|
||||
"nativescript-toasty": "^3.0.0-alpha.2",
|
||||
"nativescript-toast": "^2.0.0",
|
||||
"nativescript-ui-listview": "^8.2.0",
|
||||
"nativescript-ui-sidedrawer": "^8.0.1",
|
||||
"nativescript-vue": "^2.6.1",
|
||||
"nativescript-vue-devtools": "^1.4.0",
|
||||
"tns-core-modules": "^6.5.1",
|
||||
"vuex": "^3.3.0"
|
||||
},
|
||||
|
|