enrecipes/app/components/EnRecipes.vue

501 lines
15 KiB
Vue
Raw Normal View History

2020-09-15 11:10:16 +00:00
<template>
2020-11-10 18:28:48 +00:00
<Page @loaded="onPageLoad">
2020-10-14 19:32:32 +00:00
<ActionBar :flat="viewIsScrolled ? false : true">
<!-- Search Actionbar -->
<GridLayout
v-if="showSearch"
columns="auto, *"
2020-10-21 17:54:45 +00:00
verticalAlignment="center"
2020-10-14 19:32:32 +00:00
>
2020-11-10 18:28:48 +00:00
<MDButton
2020-11-02 11:36:53 +00:00
class="bx"
2020-10-14 19:32:32 +00:00
:text="icon.back"
2020-11-10 18:28:48 +00:00
variant="text"
2020-10-14 19:32:32 +00:00
automationText="Back"
col="0"
@tap="closeSearch"
/>
<SearchBar
col="1"
hint="Search"
v-model="searchQuery"
2020-10-21 17:54:45 +00:00
@textChange="updateFilter"
2020-11-10 18:28:48 +00:00
@clear="clearSearch"
2020-10-14 19:32:32 +00:00
/>
</GridLayout>
<!-- Home Actionbar -->
2020-11-02 11:36:53 +00:00
<GridLayout v-else columns="auto, *, auto, auto">
2020-11-10 18:28:48 +00:00
<MDButton
2020-11-02 11:36:53 +00:00
class="bx"
2020-11-10 18:28:48 +00:00
col="0"
variant="text"
@tap="showDrawer"
2020-10-14 19:32:32 +00:00
:text="icon.menu"
2020-11-02 11:36:53 +00:00
automationText="Back"
2020-10-14 19:32:32 +00:00
/>
2020-10-21 17:54:45 +00:00
<Label class="title orkm" :text="currentComponent" col="1" />
2020-11-10 18:28:48 +00:00
<MDButton
2020-10-26 20:49:54 +00:00
v-if="recipes.length"
class="bx"
:text="icon.search"
2020-11-10 18:28:48 +00:00
variant="text"
col="2"
@tap="openSearch"
/>
2020-11-10 18:28:48 +00:00
<MDButton
2020-10-26 20:49:54 +00:00
v-if="recipes.length"
class="bx"
:text="icon.sort"
2020-11-10 18:28:48 +00:00
variant="text"
col="3"
@tap="sortDialog"
/>
2020-10-14 19:32:32 +00:00
</GridLayout>
</ActionBar>
<AbsoluteLayout>
<RadListView
ref="listView"
2020-10-21 17:54:45 +00:00
itemHeight="112"
2020-10-26 20:49:54 +00:00
for="recipe in recipes"
2020-10-14 19:32:32 +00:00
swipeActions="true"
@itemSwipeProgressChanged="onSwiping"
@itemSwipeProgressEnded="onSwipeEnded"
2020-11-02 11:36:53 +00:00
@scrolled="onScroll"
2020-10-21 17:54:45 +00:00
:filteringFunction="filterFunction"
:sortingFunction="sortFunction"
2020-10-14 19:32:32 +00:00
>
<v-template>
<GridLayout
2020-11-02 11:36:53 +00:00
class="recipeItem"
2020-10-21 17:54:45 +00:00
rows="112"
columns="112, *"
androidElevation="1"
2020-09-15 11:10:16 +00:00
>
<MDRipple colSpan="2" @tap="viewRecipe(recipe.id)" />
<GridLayout class="imageHolder card" rows="112" columns="112">
<Image
row="0"
col="0"
v-if="recipe.imageSrc"
:src="recipe.imageSrc"
stretch="aspectFill"
2020-11-06 09:07:41 +00:00
decodeWidth="112"
decodeHeight="112"
loadMode="async"
/>
<Label
v-else
row="0"
col="0"
horizontalAlignment="center"
class="bx"
fontSize="56"
:text="icon.image"
/>
</GridLayout>
2020-11-02 11:36:53 +00:00
<StackLayout class="recipeInfo" col="1">
<Label :text="recipe.category" class="orkm category" />
<Label :text="recipe.title" class="orkm title" />
<StackLayout class="timeContainer" orientation="horizontal">
<Label class="bx small" :text="icon.time" />
<Label
class="time"
:text="`${formattedTime(recipe.timeRequired).time}`"
/>
</StackLayout>
2020-10-14 19:32:32 +00:00
</StackLayout>
</GridLayout>
</v-template>
<v-template name="itemswipe">
2020-10-21 17:54:45 +00:00
<GridLayout columns="*, auto">
<StackLayout id="delete-action" col="1" class="swipe-item right">
2020-10-14 19:32:32 +00:00
<Label class="bx" padding="8 0 0 6" :text="icon.trash" />
</StackLayout>
</GridLayout>
</v-template>
2020-10-21 17:54:45 +00:00
<v-template name="footer">
<StackLayout height="128"></StackLayout>
</v-template>
2020-10-14 19:32:32 +00:00
</RadListView>
2020-11-10 18:28:48 +00:00
<GridLayout rows="*, auto, *, 88" columns="*" class="emptyStateContainer">
<StackLayout
col="0"
row="1"
2020-11-10 18:28:48 +00:00
class="emptyState"
v-if="
!recipes.length &&
!filterFavorites &&
!filterTrylater &&
!selectedCategory
"
@tap="addRecipe"
>
2020-11-10 18:28:48 +00:00
<Label class="bx icon" :text="icon.plusCircle" />
<Label
2020-11-02 11:36:53 +00:00
class="title orkm"
text="Start adding your recipes!"
textWrap="true"
/>
2020-11-02 11:36:53 +00:00
<StackLayout orientation="horizontal" horizontalAlignment="center">
2020-11-10 18:28:48 +00:00
<Label text="Use the " />
2020-11-02 11:36:53 +00:00
<Label class="bx" :text="icon.plus" />
2020-11-10 18:28:48 +00:00
<Label text=" button to add one" />
2020-11-02 11:36:53 +00:00
</StackLayout>
</StackLayout>
<StackLayout
col="0"
row="1"
2020-11-10 18:28:48 +00:00
class="emptyState"
v-if="!filteredRecipes.length && filterFavorites && !searchQuery"
>
2020-11-10 18:28:48 +00:00
<Label class="bx icon" :text="icon.heartOutline" textWrap="true" />
<Label class="title orkm" text="No favorites yet" textWrap="true" />
<Label
2020-11-10 18:28:48 +00:00
text="Recipes you mark as favorite will be listed here"
textWrap="true"
/>
</StackLayout>
<StackLayout
col="0"
row="1"
2020-11-10 18:28:48 +00:00
class="emptyState"
v-if="!filteredRecipes.length && filterTrylater && !searchQuery"
>
2020-11-10 18:28:48 +00:00
<Label class="bx icon" :text="icon.trylaterOutline" textWrap="true" />
<Label class="title orkm" text="All done!" textWrap="true" />
<Label
2020-11-10 18:28:48 +00:00
text="Recipes you mark as try later will be listed here"
textWrap="true"
/>
</StackLayout>
<StackLayout
col="0"
row="1"
2020-11-10 18:28:48 +00:00
class="emptyState"
v-if="
!filteredRecipes.length &&
!filterFavorites &&
!filterTrylater &&
selectedCategory
"
>
2020-11-10 18:28:48 +00:00
<Label class="bx icon" :text="icon.labelOutline" textWrap="true" />
<Label
2020-11-02 11:36:53 +00:00
class="title orkm"
2020-11-10 18:28:48 +00:00
text="Category looks empty"
textWrap="true"
/>
2020-11-10 18:28:48 +00:00
<StackLayout orientation="horizontal" horizontalAlignment="center">
<Label text="Use the " textWrap="true" />
<Label class="bx" :text="icon.plus" />
<Label text=" button to add a recipe" textWrap="true" />
</StackLayout>
</StackLayout>
<StackLayout
col="0"
row="0"
class="emptyState noResult"
v-if="!filteredRecipes.length && searchQuery"
>
<Label class="bx icon" :text="icon.search" textWrap="true" />
<Label class="title orkm" text="No recipes found" textWrap="true" />
<Label
2020-11-10 18:28:48 +00:00
:text="
`Your search &quot;${searchQuery}&quot; did not match any recipes${
filterFavorites || filterTrylater || selectedCategory
? ' in this category'
: ''
}`
"
textWrap="true"
/>
</StackLayout>
</GridLayout>
2020-11-10 18:28:48 +00:00
<GridLayout id="btnFabContainer" rows="*, auto" columns="*, auto">
<transition name="bounce">
2020-11-10 18:28:48 +00:00
<MDFloatingActionButton
v-if="showFAB"
row="1"
col="1"
class="bx fab-button"
2020-11-10 18:28:48 +00:00
src="res://plus"
@tap="addRecipe"
/>
</transition>
2020-10-14 19:32:32 +00:00
</GridLayout>
</AbsoluteLayout>
</Page>
2020-09-15 11:10:16 +00:00
</template>
<script>
2020-11-10 18:28:48 +00:00
import { AndroidApplication, Utils } from "@nativescript/core"
import { mapActions, mapState } from "vuex"
2020-10-14 19:32:32 +00:00
import EditRecipe from "./EditRecipe.vue"
import ViewRecipe from "./ViewRecipe.vue"
2020-11-10 18:28:48 +00:00
2020-10-21 17:54:45 +00:00
import ActionDialog from "./modal/ActionDialog.vue"
import ConfirmDialog from "./modal/ConfirmDialog.vue"
2020-10-14 19:32:32 +00:00
export default {
2020-10-21 17:54:45 +00:00
props: [
"filterFavorites",
"filterTrylater",
2020-10-21 17:54:45 +00:00
"selectedCategory",
"showDrawer",
"hijackGlobalBackEvent",
"releaseGlobalBackEvent",
2020-11-06 09:07:41 +00:00
"openAppSettingsPage",
2020-10-21 17:54:45 +00:00
],
2020-10-14 19:32:32 +00:00
components: {
EditRecipe,
ViewRecipe,
},
2020-09-15 11:10:16 +00:00
data() {
return {
2020-10-14 19:32:32 +00:00
searchQuery: "",
viewIsScrolled: false,
2020-10-21 17:54:45 +00:00
showSearch: false,
2020-10-14 19:32:32 +00:00
rightAction: false,
2020-10-21 17:54:45 +00:00
sortType: "Natural order",
deletionDialogActive: false,
showFAB: false,
2020-09-15 11:10:16 +00:00
}
},
computed: {
2020-10-26 20:49:54 +00:00
...mapState(["icon", "recipes", "currentComponent"]),
2020-09-15 11:10:16 +00:00
filteredRecipes() {
2020-10-21 17:54:45 +00:00
if (this.filterFavorites) {
2020-10-26 20:49:54 +00:00
return this.recipes.filter(
2020-10-21 17:54:45 +00:00
(e) =>
e.isFavorite && e.title.toLowerCase().includes(this.searchQuery)
)
} else if (this.filterTrylater) {
2020-10-26 20:49:54 +00:00
return this.recipes.filter(
2020-10-21 17:54:45 +00:00
(e) => !e.tried && e.title.toLowerCase().includes(this.searchQuery)
)
} else if (this.selectedCategory) {
2020-10-26 20:49:54 +00:00
return this.recipes.filter(
2020-10-21 17:54:45 +00:00
(e) =>
e.category === this.selectedCategory &&
e.title.toLowerCase().includes(this.searchQuery)
)
2020-09-15 18:04:33 +00:00
} else {
2020-10-26 20:49:54 +00:00
return this.recipes.filter((e) =>
2020-10-21 17:54:45 +00:00
e.title.toLowerCase().includes(this.searchQuery)
)
2020-09-15 11:10:16 +00:00
}
},
},
methods: {
2020-10-22 18:36:50 +00:00
...mapActions(["setCurrentComponentAction", "deleteRecipeAction"]),
2020-11-10 18:28:48 +00:00
onPageLoad() {
this.filterFavorites
? this.setComponent("Favorites")
: this.filterTrylater
2020-11-15 21:13:06 +00:00
? this.setComponent("Try Later")
: this.selectedCategory
? this.setComponent(this.selectedCategory)
: this.setComponent("EnRecipes")
this.showFAB = true
},
2020-11-10 18:28:48 +00:00
// HELPERS
2020-10-21 17:54:45 +00:00
openSearch() {
this.showSearch = true
2020-11-10 18:28:48 +00:00
this.showFAB = false
2020-10-21 17:54:45 +00:00
this.hijackLocalBackEvent()
},
2020-11-10 18:28:48 +00:00
closeSearch() {
if (this.searchQuery) this.updateFilter()
this.searchQuery = ""
Utils.ad.dismissSoftInput()
this.showSearch = false
this.showFAB = true
this.releaseLocalBackEvent()
},
setComponent(comp) {
this.setCurrentComponentAction(comp)
this.hijackGlobalBackEvent()
},
clearSearch() {
if (this.searchQuery !== "") {
this.updateFilter()
}
},
formattedTime(time) {
let t = time.split(":")
let h = parseInt(t[0])
let m = parseInt(t[1])
return {
2020-11-11 13:50:33 +00:00
time: h ? (m ? `${h} hr ${m} min` : `${h} hr`) : `${m} min`,
2020-11-10 18:28:48 +00:00
duration: `${h}${m}`,
}
},
onScroll(args) {
args.scrollOffset
? (this.viewIsScrolled = true)
: (this.viewIsScrolled = false)
},
// NAVIGATION HANDLERS
2020-10-21 17:54:45 +00:00
hijackLocalBackEvent() {
this.releaseGlobalBackEvent()
2020-10-22 18:36:50 +00:00
AndroidApplication.on(
AndroidApplication.activityBackPressedEvent,
2020-10-21 17:54:45 +00:00
this.searchBackEvent
)
},
releaseLocalBackEvent() {
2020-10-22 18:36:50 +00:00
AndroidApplication.off(
AndroidApplication.activityBackPressedEvent,
2020-10-21 17:54:45 +00:00
this.searchBackEvent
)
this.hijackGlobalBackEvent()
},
searchBackEvent(args) {
args.cancel = true
this.closeSearch()
},
2020-11-10 18:28:48 +00:00
addRecipe() {
this.showFAB = false
this.releaseGlobalBackEvent()
this.$navigateTo(EditRecipe, {
props: {
selectedCategory: this.selectedCategory,
openAppSettingsPage: this.openAppSettingsPage,
filterFavorites: this.filterFavorites,
},
})
2020-10-21 17:54:45 +00:00
},
viewRecipe(recipeID) {
2020-11-10 18:28:48 +00:00
this.showFAB = false
this.$navigateTo(ViewRecipe, {
// transition: {
// name: "fade",
// duration: 200,
// curve: "easeOut",
// },
props: {
filterTrylater: this.filterTrylater,
recipeID,
2020-11-10 18:28:48 +00:00
hijackGlobalBackEvent: this.hijackGlobalBackEvent,
releaseGlobalBackEvent: this.releaseGlobalBackEvent,
},
})
},
// LIST HANDLERS
2020-10-21 17:54:45 +00:00
sortDialog() {
this.releaseGlobalBackEvent()
this.$showModal(ActionDialog, {
props: {
title: "Sort by",
list: ["Natural order", "Title", "Duration", "Last modified"],
2020-11-10 18:28:48 +00:00
height: "225",
2020-10-21 17:54:45 +00:00
},
}).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,
})
2020-11-02 11:36:53 +00:00
let d1 = this.formattedTime(item.timeRequired).duration
let d2 = this.formattedTime(otherItem.timeRequired).duration
2020-10-21 17:54:45 +00:00
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
}
},
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.filterTrylater) {
2020-10-21 17:54:45 +00:00
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)
}
},
2020-10-14 19:32:32 +00:00
onSwiping({ data, object }) {
const swipeLimits = data.swipeLimits
const swipeView = object
const rightItem = swipeView.getViewById("delete-action")
swipeLimits.right = rightItem.getMeasuredWidth() - 12
2020-10-21 17:54:45 +00:00
swipeLimits.threshold = swipeLimits.right - 6
if (data.x < -swipeLimits.threshold) {
2020-10-14 19:32:32 +00:00
this.rightAction = true
2020-10-21 17:54:45 +00:00
swipeView.notifySwipeToExecuteFinished()
2020-10-14 19:32:32 +00:00
}
},
onSwipeEnded({ index }) {
2020-10-26 20:49:54 +00:00
let recipeID = this.recipes[index].id
if (this.rightAction && !this.deletionDialogActive)
this.deleteRecipe(index, recipeID)
2020-10-21 17:54:45 +00:00
this.rightAction = false
2020-10-14 19:32:32 +00:00
},
2020-11-10 18:28:48 +00:00
// DATA HANDLERS
deleteRecipe(index, recipeID) {
this.deletionDialogActive = true
2020-10-21 17:54:45 +00:00
this.$showModal(ConfirmDialog, {
props: {
title: "Delete recipe",
2020-10-26 20:49:54 +00:00
description: `Are you sure you want to delete the recipe "${this.recipes[index].title}"?`,
2020-10-21 17:54:45 +00:00
cancelButtonText: "CANCEL",
okButtonText: "DELETE",
},
}).then((action) => {
if (action) {
2020-10-26 20:49:54 +00:00
this.deleteRecipeAction({ index, id: recipeID })
2020-10-21 17:54:45 +00:00
}
this.deletionDialogActive = false
2020-10-21 17:54:45 +00:00
})
2020-10-14 19:32:32 +00:00
},
2020-09-15 11:10:16 +00:00
},
mounted() {
this.showFAB = true
},
2020-09-15 11:10:16 +00:00
}
</script>