2020-09-15 11:10:16 +00:00
< template >
2020-12-07 14:45:00 +00:00
< Page @loaded ="onPageLoad" @unloaded ="onPageUnload" >
< ActionBar : androidElevation = "viewIsScrolled ? 4 : 0" >
< GridLayout v-if = "showSearch" columns="auto, *" verticalAlignment="center" >
< MDButton class = "bx" :text = "icon.back" variant = "text" automationText = "Back" col = "0" @tap ="closeSearch" / >
< SearchBar col = "1" : hint = "'Search' | L" v-model = "searchQuery" @textChange="updateFilter" @clear="clearSearch" / >
< / GridLayout >
< GridLayout v -else columns = "auto, *, auto, auto" >
< MDButton class = "bx" col = "0" variant = "text" @tap ="showDrawer" :text = "icon.menu" automationText = "Back" / >
< Label class = "title orkm" : text = "`${currentComponent}` | L" col = "1" / >
< MDButton v-if = "recipes.length" class="bx" :text="icon.search" variant="text" col="2" @tap="openSearch" / >
< MDButton v-if = "recipes.length" class="bx" :text="icon.sort" variant="text" col="3" @tap="sortDialog" / >
< / GridLayout >
< / ActionBar >
< AbsoluteLayout >
< RadListView ref = "listView" itemHeight = "112" for = "recipe in recipes" swipeActions = "true" @itemSwipeProgressChanged ="onSwiping" @itemSwipeProgressEnded ="onSwipeEnded" @scrolled ="onScroll" :filteringFunction = "filterFunction"
: sortingFunction = "sortFunction" >
< v-template >
< GridLayout class = "recipeItem" rows = "112" columns = "112, *" androidElevation = "1" >
< 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" decodeWidth="112" decodeHeight="112" loadMode="async" / >
< Label v -else row = "0" col = "0" horizontalAlignment = "center" class = "bx" fontSize = "56" :text = "icon.image" / >
< / GridLayout >
< StackLayout class = "recipeInfo" col = "1" >
< Label : text = "`${recipe.category}` | L" 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 = "
2020-12-01 09:22:22 +00:00
` ${
formattedTotalTime ( recipe . prepTime , recipe . cookTime ) . time
} `
2020-12-07 14:45:00 +00:00
" / >
2020-10-14 19:32:32 +00:00
< / StackLayout >
2020-12-07 14:45:00 +00:00
< / StackLayout >
< / GridLayout >
< / v-template >
< v-template name = "itemswipe" >
< 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 = "80" > < / StackLayout >
< / v-template >
< / RadListView >
< GridLayout rows = "*, auto, *, 88" columns = "*" class = "emptyStateContainer" >
< StackLayout row = "1" class = "emptyState" v -if = "
2020-11-10 18:28:48 +00:00
! recipes . length &&
2020-12-07 14:45:00 +00:00
! filterFavourites &&
2020-11-10 18:28:48 +00:00
! filterTrylater &&
! selectedCategory
2020-12-07 14:45:00 +00:00
" @tap=" addRecipe " >
< Label class = "bx icon" :text = "icon.plusCircle" / >
< Label class = "title orkm" : text = "'Start adding your recipes!' | L" textWrap = "true" / >
< StackLayout orientation = "horizontal" horizontalAlignment = "center" >
< Label : text = "'Use the plus button to add one' | L" textWrap = "true" / >
2020-11-10 18:28:48 +00:00
< / StackLayout >
2020-12-07 14:45:00 +00:00
< / StackLayout >
< StackLayout row = "1" class = "emptyState" v-if = "!filteredRecipes.length && filterTrylater && !searchQuery" >
< Label class = "bx icon" :text = "icon.trylaterOutline" textWrap = "true" / >
< Label class = "title orkm" : text = "'All done!' | L" textWrap = "true" / >
< Label : text = "'Recipes you mark as try later will be listed here' | L" textWrap = "true" / >
< / StackLayout >
< StackLayout row = "1" class = "emptyState" v-if = "!filteredRecipes.length && filterFavourites && !searchQuery" >
< Label class = "bx icon" :text = "icon.heartOutline" textWrap = "true" / >
< Label class = "title orkm" : text = "'No favourites yet' | L" textWrap = "true" / >
< Label : text = "'Recipes you mark as favourite will be listed here' | L" textWrap = "true" / >
< / StackLayout >
< StackLayout row = "1" class = "emptyState" v-if = "!filteredRecipes.length && !searchQuery && selectedCategory" >
< Label class = "bx icon" :text = "icon.labelOutline" textWrap = "true" / >
< Label class = "title orkm" : text = "'Category looks empty' | L" textWrap = "true" / >
< StackLayout orientation = "horizontal" horizontalAlignment = "center" >
< Label : text = "'Use the plus button to add one' | L" textWrap = "true" / >
2020-10-29 20:12:53 +00:00
< / StackLayout >
2020-12-07 14:45:00 +00:00
< / StackLayout >
< StackLayout row = "1" class = "emptyState" v-if = "!filteredRecipes.length && searchQuery" >
< Label class = "bx icon" :text = "icon.search" textWrap = "true" / >
< Label class = "title orkm" : text = "'No recipes found' | L" textWrap = "true" / >
< Label : text = "`${noResultFor}` | L" textWrap = "true" / >
< / StackLayout >
< / GridLayout >
< GridLayout id = "btnFabContainer" rows = "*, auto" columns = "*, auto" >
< transition name = "bounce" >
< MDFloatingActionButton v-if = "showFAB" row="1" col="1" class="bx fab-button" src="res://plus" @tap="addRecipe" / >
< / transition >
< / GridLayout >
< / AbsoluteLayout >
< / Page >
2020-09-15 11:10:16 +00:00
< / template >
< script >
2020-12-07 14:45:00 +00:00
import {
AndroidApplication ,
Utils ,
Observable
}
from "@nativescript/core" ;
import {
localize
}
from "@nativescript/localize"
import {
mapActions ,
mapState
}
from "vuex" ;
import EditRecipe from "./EditRecipe.vue" ;
import ViewRecipe from "./ViewRecipe.vue" ;
import ActionDialog from "./modal/ActionDialog.vue" ;
import ConfirmDialog from "./modal/ConfirmDialog.vue" ;
import * as utils from "~/shared/utils" ;
2020-10-14 19:32:32 +00:00
export default {
2020-12-07 14:45:00 +00:00
props : [ "filterFavourites" , "filterTrylater" , "closeDrawer" , "selectedCategory" , "hijackGlobalBackEvent" , "releaseGlobalBackEvent" ] ,
2020-10-14 19:32:32 +00:00
components : {
EditRecipe ,
2020-12-07 14:45:00 +00:00
ViewRecipe
2020-10-14 19:32:32 +00:00
} ,
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" ,
2020-10-24 18:02:35 +00:00
deletionDialogActive : false ,
2020-12-07 14:45:00 +00:00
showFAB : false
} ;
2020-09-15 11:10:16 +00:00
} ,
computed : {
2020-12-07 14:45:00 +00:00
... mapState ( [ "icon" , "recipes" , "currentComponent" ] ) ,
2020-09-15 11:10:16 +00:00
filteredRecipes ( ) {
2020-12-07 14:45:00 +00:00
if ( this . filterFavourites ) {
return this . recipes . filter ( e => e . isFavorite && e . title . toLowerCase ( ) . includes ( this . searchQuery ) ) ;
}
else if ( this . filterTrylater ) {
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 => e . title . toLowerCase ( ) . includes ( this . searchQuery ) ) ;
2020-09-15 11:10:16 +00:00
}
} ,
2020-11-28 19:21:57 +00:00
noResultFor ( ) {
2020-12-07 14:45:00 +00:00
if ( this . selectedCategory ) return "Your search did not match any recipes in this category" ;
if ( this . filterFavourites ) return "Your search did not match any recipes in your favourites" ;
if ( this . filterTrylater ) return "Your search did not match any recipes in your try later list" ;
return "Your search did not match any recipes" ;
}
2020-09-15 11:10:16 +00:00
} ,
methods : {
2020-12-07 14:45:00 +00:00
... mapActions ( [ "setCurrentComponentAction" , "deleteRecipeAction" ] ) ,
onPageLoad ( args ) {
const page = args . object ;
page . bindingContext = new Observable ( ) ;
this . filterFavourites ? this . setComponent ( "Favourites" ) : this . filterTrylater ? this . setComponent ( "Try Later" ) : this . selectedCategory ? this . setComponent ( this . selectedCategory ) : this . setComponent ( "EnRecipes" ) ;
this . showFAB = true ;
} ,
onPageUnload ( ) {
this . releaseGlobalBackEvent ( ) ;
2020-10-24 18:02:35 +00:00
} ,
2020-11-10 18:28:48 +00:00
// HELPERS
2020-11-23 09:49:58 +00:00
showDrawer ( ) {
2020-12-07 14:45:00 +00:00
utils . showDrawer ( ) ;
2020-11-23 09:49:58 +00:00
} ,
2020-10-21 17:54:45 +00:00
openSearch ( ) {
2020-12-07 14:45:00 +00:00
this . showSearch = true ;
this . showFAB = false ;
this . hijackLocalBackEvent ( ) ;
2020-10-21 17:54:45 +00:00
} ,
2020-11-10 18:28:48 +00:00
closeSearch ( ) {
2020-12-07 14:45:00 +00:00
if ( this . searchQuery ) this . updateFilter ( ) ;
this . searchQuery = "" ;
Utils . ad . dismissSoftInput ( ) ;
this . showSearch = false ;
this . showFAB = true ;
this . releaseLocalBackEvent ( ) ;
2020-11-10 18:28:48 +00:00
} ,
2020-12-07 14:45:00 +00:00
setComponent ( comp ) {
this . setCurrentComponentAction ( comp ) ;
this . hijackGlobalBackEvent ( ) ;
2020-11-10 18:28:48 +00:00
} ,
clearSearch ( ) {
2020-12-07 14:45:00 +00:00
if ( this . searchQuery !== "" ) {
this . updateFilter ( ) ;
2020-11-10 18:28:48 +00:00
}
} ,
2020-12-07 14:45:00 +00:00
formattedTotalTime ( prepTime , cookTime ) {
let t1 = prepTime . split ( ":" ) ;
let t2 = cookTime . split ( ":" ) ;
let h = parseInt ( t1 [ 0 ] ) + parseInt ( t2 [ 0 ] ) ;
let m = parseInt ( t1 [ 1 ] ) + parseInt ( t2 [ 1 ] ) ;
let hr = localize ( 'hr' )
let min = localize ( 'min' )
2020-11-10 18:28:48 +00:00
return {
2020-12-07 14:45:00 +00:00
time : h ? ( m ? ` ${ h } ${ hr } ${ m } ${ min } ` : ` ${ h } ${ hr } ` ) : ` ${ m } ${ min } ` ,
duration : ` ${ h } ${ m } `
} ;
2020-11-10 18:28:48 +00:00
} ,
2020-12-07 14:45:00 +00:00
onScroll ( args ) {
this . viewIsScrolled = args . scrollOffset ? true : false ;
2020-11-10 18:28:48 +00:00
} ,
// NAVIGATION HANDLERS
2020-10-21 17:54:45 +00:00
hijackLocalBackEvent ( ) {
2020-12-07 14:45:00 +00:00
this . releaseGlobalBackEvent ( ) ;
AndroidApplication . on ( AndroidApplication . activityBackPressedEvent , this . searchBackEvent ) ;
2020-10-21 17:54:45 +00:00
} ,
releaseLocalBackEvent ( ) {
2020-12-07 14:45:00 +00:00
AndroidApplication . off ( AndroidApplication . activityBackPressedEvent , this . searchBackEvent ) ;
this . hijackGlobalBackEvent ( ) ;
2020-10-21 17:54:45 +00:00
} ,
2020-12-07 14:45:00 +00:00
searchBackEvent ( args ) {
args . cancel = true ;
this . closeDrawer ( ) ;
this . closeSearch ( ) ;
2020-10-21 17:54:45 +00:00
} ,
2020-11-10 18:28:48 +00:00
addRecipe ( ) {
2020-12-07 14:45:00 +00:00
this . showFAB = false ;
this . releaseGlobalBackEvent ( ) ;
this . $navigateTo ( EditRecipe , {
2020-11-10 18:28:48 +00:00
props : {
selectedCategory : this . selectedCategory ,
2020-12-07 14:45:00 +00:00
filterFavourites : this . filterFavourites
}
} ) ;
2020-10-21 17:54:45 +00:00
} ,
2020-12-07 14:45:00 +00:00
viewRecipe ( recipeID ) {
this . showFAB = false ;
this . $navigateTo ( ViewRecipe , {
2020-11-10 18:28:48 +00:00
props : {
filterTrylater : this . filterTrylater ,
2020-12-07 14:45:00 +00:00
recipeID
2020-11-10 18:28:48 +00:00
} ,
2020-12-07 14:45:00 +00:00
backstackVisible : false
} ) ;
2020-11-10 18:28:48 +00:00
} ,
// LIST HANDLERS
2020-10-21 17:54:45 +00:00
sortDialog ( ) {
2020-12-07 14:45:00 +00:00
this . releaseGlobalBackEvent ( ) ;
this . $showModal ( ActionDialog , {
2020-10-21 17:54:45 +00:00
props : {
title : "Sort by" ,
2020-12-07 14:45:00 +00:00
list : [ "Natural order" , "Title" , "Duration" , "Last modified" ] ,
height : "192"
}
} ) . then ( action => {
if ( action && action !== "Cancel" && this . sortType !== action ) {
this . sortType = action ;
this . updateSort ( ) ;
2020-10-21 17:54:45 +00:00
}
2020-12-07 14:45:00 +00:00
this . hijackGlobalBackEvent ( ) ;
} ) ;
2020-10-21 17:54:45 +00:00
} ,
updateSort ( ) {
2020-12-07 14:45:00 +00:00
let listView = this . $refs . listView . nativeView ;
listView . sortingFunction = undefined ;
listView . sortingFunction = this . sortFunction ;
2020-10-21 17:54:45 +00:00
} ,
2020-12-07 14:45:00 +00:00
sortFunction ( item , otherItem ) {
const titleOrder = item . title . toLowerCase ( ) . localeCompare ( otherItem . title . toLowerCase ( ) , "en" , {
ignorePunctuation : true
} ) ;
let d1 = this . formattedTotalTime ( item . prepTime , item . cookTime ) . duration ;
let d2 = this . formattedTotalTime ( otherItem . prepTime , otherItem . cookTime ) . duration ;
let ld1 = new Date ( item . lastModified ) ;
let ld2 = new Date ( otherItem . lastModified ) ;
switch ( this . sortType ) {
2020-10-21 17:54:45 +00:00
case "Title" :
2020-12-07 14:45:00 +00:00
return titleOrder > 0 ? - 1 : titleOrder < 0 ? 1 : 0 ;
break ;
2020-10-21 17:54:45 +00:00
case "Duration" :
2020-12-07 14:45:00 +00:00
return d1 > d2 ? - 1 : d1 < d2 ? 1 : 0 ;
break ;
2020-10-21 17:54:45 +00:00
case "Last modified" :
2020-12-07 14:45:00 +00:00
return ld1 < ld2 ? - 1 : ld1 > ld2 ? 1 : 0 ;
break ;
2020-10-21 17:54:45 +00:00
default :
2020-12-07 14:45:00 +00:00
return 0 ;
break ;
2020-10-21 17:54:45 +00:00
}
} ,
updateFilter ( ) {
2020-12-07 14:45:00 +00:00
let listView = this . $refs . listView . nativeView ;
setTimeout ( e => {
listView . filteringFunction = undefined ;
listView . filteringFunction = this . filterFunction ;
} , 1 ) ;
2020-10-21 17:54:45 +00:00
} ,
2020-12-07 14:45:00 +00:00
filterFunction ( item ) {
if ( this . filterFavourites ) {
return item . isFavorite ? item . title . toLowerCase ( ) . includes ( this . searchQuery ) : false ;
}
else if ( this . filterTrylater ) {
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-21 17:54:45 +00:00
}
} ,
2020-12-07 14:45:00 +00:00
onSwiping ( {
data ,
object
} ) {
const swipeLimits = data . swipeLimits ;
const swipeView = object ;
const rightItem = swipeView . getViewById ( "delete-action" ) ;
swipeLimits . right = rightItem . getMeasuredWidth ( ) - 12 ;
swipeLimits . threshold = swipeLimits . right - 6 ;
if ( data . x < - swipeLimits . threshold ) {
this . rightAction = true ;
swipeView . notifySwipeToExecuteFinished ( ) ;
2020-10-14 19:32:32 +00:00
}
} ,
2020-12-07 14:45:00 +00:00
onSwipeEnded ( {
index
} ) {
let recipeID = this . recipes [ index ] . id ;
if ( this . rightAction && ! this . deletionDialogActive ) this . deleteRecipe ( index , recipeID ) ;
this . rightAction = false ;
2020-10-14 19:32:32 +00:00
} ,
2020-11-10 18:28:48 +00:00
// DATA HANDLERS
2020-12-07 14:45:00 +00:00
deleteRecipe ( index , recipeID ) {
this . deletionDialogActive = true ;
this . $showModal ( ConfirmDialog , {
2020-10-21 17:54:45 +00:00
props : {
2020-12-07 14:45:00 +00:00
title : localize ( "Delete recipe?" ) ,
description : ` ${ localize ( 'Are you sure you want to delete the recipe' ) } " ${ this . recipes [ index ] . title } "? ` ,
2020-10-21 17:54:45 +00:00
cancelButtonText : "CANCEL" ,
2020-12-07 14:45:00 +00:00
okButtonText : "DELETE"
2020-10-21 17:54:45 +00:00
}
2020-12-07 14:45:00 +00:00
} ) . then ( action => {
if ( action ) {
this . deleteRecipeAction ( {
index ,
id : recipeID
} ) ;
}
this . deletionDialogActive = false ;
} ) ;
}
2020-09-15 11:10:16 +00:00
} ,
2020-10-29 20:12:53 +00:00
mounted ( ) {
2020-12-07 14:45:00 +00:00
this . showFAB = true ;
}
} ;
2020-09-15 11:10:16 +00:00
< / script >