872 lines
26 KiB
Vue
872 lines
26 KiB
Vue
<template>
|
|
<Page @loaded="onPageLoad" @unloaded="onPageUnload">
|
|
<ActionBar androidElevation="1">
|
|
<GridLayout
|
|
v-if="showSearch"
|
|
columns="auto, *"
|
|
verticalAlignment="center"
|
|
>
|
|
<MDButton
|
|
class="er"
|
|
:text="icon.back"
|
|
variant="text"
|
|
automationText="Back"
|
|
col="0"
|
|
@tap="closeSearch"
|
|
/>
|
|
<SearchBar
|
|
col="1"
|
|
:hint="'ser' | L"
|
|
@textChange="updateList($event.value)"
|
|
@clear="clearSearch"
|
|
/>
|
|
</GridLayout>
|
|
<GridLayout v-else columns="auto, *, auto, auto, auto">
|
|
<MDButton
|
|
class="er"
|
|
col="0"
|
|
variant="text"
|
|
@tap="selectMode ? clearSelection() : showDrawer()"
|
|
:text="selectMode ? icon.x : icon.menu"
|
|
/>
|
|
<Label
|
|
v-if="selectMode"
|
|
class="title orkm"
|
|
:text="`${selection.length} ${$options.filters.L('sltd')}`"
|
|
col="1"
|
|
/>
|
|
<Label
|
|
v-else
|
|
class="title orkm"
|
|
:text="`${currentComponent}` | L"
|
|
col="1"
|
|
/>
|
|
<MDButton
|
|
v-if="recipes.length"
|
|
class="er mdr"
|
|
:text="layout == 1 ? icon.l1 : layout == 2 ? icon.l2 : icon.l3"
|
|
variant="text"
|
|
col="2"
|
|
@tap="switchLayout"
|
|
/>
|
|
<MDButton
|
|
v-if="recipes.length && !selectMode"
|
|
class="er"
|
|
:text="selectMode ? icon.export : icon.sear"
|
|
variant="text"
|
|
col="3"
|
|
@tap="selectMode ? exportSelection() : openSearch()"
|
|
/>
|
|
<MDButton
|
|
v-if="recipes.length"
|
|
class="er"
|
|
:text="selectMode ? icon.del : icon.sort"
|
|
variant="text"
|
|
col="4"
|
|
@tap="selectMode ? deleteSelection() : sortDialog()"
|
|
/>
|
|
</GridLayout>
|
|
</ActionBar>
|
|
<AbsoluteLayout>
|
|
<CollectionView
|
|
width="100%"
|
|
height="100%"
|
|
for="recipe in filteredRecipes"
|
|
ref="listview"
|
|
:itemTemplateSelector="getLayout"
|
|
>
|
|
<v-template name="one">
|
|
<GridLayout class="recipeContainer" :class="isFirstItem(recipe.id)">
|
|
<GridLayout
|
|
class="recipeItem layout1 mdr"
|
|
rows="104"
|
|
columns="104, *"
|
|
androidElevation="1"
|
|
ref="recipe"
|
|
@longPress="
|
|
selectMode
|
|
? viewRecipe(recipe.id)
|
|
: addToSelection($event, recipe.id)
|
|
"
|
|
@tap="
|
|
selectMode
|
|
? addToSelection($event, recipe.id)
|
|
: viewRecipe(recipe.id)
|
|
"
|
|
>
|
|
<GridLayout class="imageHolder card" rows="104" columns="104">
|
|
<Image
|
|
row="0"
|
|
col="0"
|
|
v-if="recipe.imageSrc"
|
|
:src="recipe.imageSrc"
|
|
stretch="aspectFill"
|
|
decodeWidth="104"
|
|
decodeHeight="104"
|
|
loadMode="async"
|
|
/>
|
|
<Label
|
|
v-else
|
|
row="0"
|
|
col="0"
|
|
horizontalAlignment="center"
|
|
class="er"
|
|
fontSize="56"
|
|
:text="icon.img"
|
|
/>
|
|
</GridLayout>
|
|
<StackLayout class="recipeInfo" col="1">
|
|
<Label
|
|
:text="`${$options.filters.L(
|
|
recipe.cuisine
|
|
)} • ${$options.filters.L(recipe.category)}`"
|
|
class="category"
|
|
/>
|
|
<Label :text="recipe.title" class="orkm title" />
|
|
<GridLayout columns="*" rows="auto, *, auto">
|
|
<StackLayout
|
|
class="attrContainer"
|
|
orientation="horizontal"
|
|
row="0"
|
|
>
|
|
<Label class="er small" :text="icon.star" />
|
|
<Label class="attr" :text="recipe.rating" />
|
|
<Label class="er small" :text="icon.diff" />
|
|
<Label class="attr" :text="`${recipe.difficulty}` | L" />
|
|
<Label class="er small" :text="icon.time" />
|
|
<Label
|
|
class="attr"
|
|
:text="`${
|
|
formattedTotalTime(recipe.prepTime, recipe.cookTime)
|
|
.time
|
|
}`"
|
|
/>
|
|
</StackLayout>
|
|
<StackLayout
|
|
class="tagsContainer"
|
|
orientation="horizontal"
|
|
row="2"
|
|
>
|
|
<Label
|
|
v-for="(tag, index) in recipe.tags"
|
|
:key="index"
|
|
class="tag"
|
|
:text="tag"
|
|
/>
|
|
</StackLayout>
|
|
</GridLayout>
|
|
</StackLayout>
|
|
</GridLayout>
|
|
</GridLayout>
|
|
</v-template>
|
|
<v-template name="two">
|
|
<GridLayout class="recipeContainer" :class="isFirstItem(recipe.id)">
|
|
<GridLayout
|
|
class="recipeItem layout2 mdr"
|
|
rows="auto, auto"
|
|
columns="*"
|
|
androidElevation="1"
|
|
ref="recipe"
|
|
@longPress="
|
|
selectMode
|
|
? viewRecipe(recipe.id)
|
|
: addToSelection($event, recipe.id)
|
|
"
|
|
@tap="
|
|
selectMode
|
|
? addToSelection($event, recipe.id)
|
|
: viewRecipe(recipe.id)
|
|
"
|
|
>
|
|
<GridLayout class="imageHolder card" :rows="imgWidth" columns="*">
|
|
<Image
|
|
row="0"
|
|
col="0"
|
|
v-if="recipe.imageSrc"
|
|
:src="recipe.imageSrc"
|
|
stretch="aspectFill"
|
|
:decodeWidth="imgWidth"
|
|
:decodeHeight="imgWidth"
|
|
loadMode="async"
|
|
/>
|
|
<Label
|
|
v-else
|
|
row="0"
|
|
col="0"
|
|
horizontalAlignment="center"
|
|
class="er"
|
|
:fontSize="imgWidth / 2"
|
|
:text="icon.img"
|
|
/>
|
|
</GridLayout>
|
|
<StackLayout class="recipeInfo" row="1">
|
|
<Label
|
|
:text="`${$options.filters.L(
|
|
recipe.cuisine
|
|
)} • ${$options.filters.L(recipe.category)}`"
|
|
class="category"
|
|
/>
|
|
<Label :text="recipe.title" class="orkm title" />
|
|
<StackLayout
|
|
class="tagsContainer"
|
|
orientation="horizontal"
|
|
row="2"
|
|
>
|
|
<Label
|
|
v-for="(tag, index) in recipe.tags"
|
|
:key="index"
|
|
v-if="tag"
|
|
class="tag"
|
|
:text="tag"
|
|
/>
|
|
</StackLayout>
|
|
</StackLayout>
|
|
</GridLayout>
|
|
</GridLayout>
|
|
</v-template>
|
|
<v-template name="three">
|
|
<GridLayout class="recipeContainer" :class="isFirstItem(recipe.id)">
|
|
<GridLayout
|
|
class="recipeItem layout1 mdr"
|
|
rows="auto"
|
|
columns="*"
|
|
androidElevation="1"
|
|
ref="recipe"
|
|
@longPress="
|
|
selectMode
|
|
? viewRecipe(recipe.id)
|
|
: addToSelection($event, recipe.id)
|
|
"
|
|
@tap="
|
|
selectMode
|
|
? addToSelection($event, recipe.id)
|
|
: viewRecipe(recipe.id)
|
|
"
|
|
>
|
|
<StackLayout class="recipeInfo">
|
|
<Label
|
|
:text="`${$options.filters.L(
|
|
recipe.cuisine
|
|
)} • ${$options.filters.L(recipe.category)}`"
|
|
class="category"
|
|
/>
|
|
<Label :text="recipe.title" class="orkm title" />
|
|
<StackLayout class="tagsContainer" orientation="horizontal">
|
|
<Label
|
|
v-for="(tag, index) in recipe.tags"
|
|
:key="index"
|
|
v-if="tag"
|
|
class="tag"
|
|
:text="tag"
|
|
/>
|
|
</StackLayout>
|
|
</StackLayout>
|
|
</GridLayout>
|
|
</GridLayout>
|
|
</v-template>
|
|
</CollectionView>
|
|
<GridLayout rows="*, auto, *, 88" columns="*" class="emptyStateContainer">
|
|
<StackLayout
|
|
row="1"
|
|
class="emptyState"
|
|
v-if="!recipes.length && !filterFavourites && !filterTrylater"
|
|
@tap="addRecipe"
|
|
>
|
|
<Label class="er icon" :text="icon.plusc" />
|
|
<Label class="title orkm" :text="'strAdd' | L" textWrap="true" />
|
|
<StackLayout orientation="horizontal" horizontalAlignment="center">
|
|
<Label :text="'plsAdd' | L" textWrap="true" />
|
|
</StackLayout>
|
|
</StackLayout>
|
|
<StackLayout
|
|
row="1"
|
|
class="emptyState"
|
|
v-if="!filteredRecipes.length && filterTrylater && !searchQuery"
|
|
>
|
|
<Label class="er icon" :text="icon.try" textWrap="true" />
|
|
<Label class="title orkm" :text="'aD' | L" textWrap="true" />
|
|
<Label :text="'tLInfo' | L" textWrap="true" />
|
|
</StackLayout>
|
|
<StackLayout
|
|
row="1"
|
|
class="emptyState"
|
|
v-if="!filteredRecipes.length && filterFavourites && !searchQuery"
|
|
>
|
|
<Label class="er icon" :text="icon.fav" textWrap="true" />
|
|
<Label class="title orkm" :text="'noFavs' | L" textWrap="true" />
|
|
<Label :text="'fsList' | L" textWrap="true" />
|
|
</StackLayout>
|
|
<StackLayout
|
|
row="1"
|
|
class="emptyState"
|
|
v-if="!filteredRecipes.length && searchQuery"
|
|
>
|
|
<Label class="er icon" :text="icon.noresult" textWrap="true" />
|
|
<Label
|
|
class="title orkm"
|
|
:text="`${noResultFor}` | L"
|
|
textWrap="true"
|
|
/>
|
|
<MDButton
|
|
v-if="filterFavourites || filterTrylater || selectedCuisine"
|
|
variant="text"
|
|
class="searchAll orkm"
|
|
:text="'trySer' | L"
|
|
@tap="searchAll"
|
|
/>
|
|
</StackLayout>
|
|
</GridLayout>
|
|
<GridLayout id="btnFabContainer" rows="*, auto" columns="*, auto">
|
|
<transition name="bounce">
|
|
<MDFloatingActionButton
|
|
v-if="showFAB"
|
|
row="1"
|
|
col="1"
|
|
class="er fab-button"
|
|
src="res://plus"
|
|
@tap="addRecipe"
|
|
/>
|
|
</transition>
|
|
</GridLayout>
|
|
</AbsoluteLayout>
|
|
</Page>
|
|
</template>
|
|
<script>
|
|
import {
|
|
ApplicationSettings,
|
|
AndroidApplication,
|
|
Utils,
|
|
Observable,
|
|
Device,
|
|
Screen,
|
|
} from "@nativescript/core";
|
|
import { localize } from "@nativescript/localize";
|
|
import { time } from "tns-core-modules/profiling";
|
|
import {
|
|
startAccelerometerUpdates,
|
|
stopAccelerometerUpdates,
|
|
} from "nativescript-accelerometer";
|
|
import { Vibrate } from "nativescript-vibrate";
|
|
let vibrator = new Vibrate();
|
|
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.js";
|
|
let lastTime = 0;
|
|
let lastShake = 0;
|
|
let lastForce = 0;
|
|
let shakeCount = 0;
|
|
let typingTimer;
|
|
export default {
|
|
props: [
|
|
"filterFavourites",
|
|
"filterTrylater",
|
|
"closeDrawer",
|
|
"showDrawer",
|
|
"selectedCategory",
|
|
"selectedCuisine",
|
|
"selectedTag",
|
|
"hijackGlobalBackEvent",
|
|
"releaseGlobalBackEvent",
|
|
],
|
|
components: {
|
|
EditRecipe,
|
|
ViewRecipe,
|
|
},
|
|
data() {
|
|
return {
|
|
searchQuery: "",
|
|
showSearch: false,
|
|
rightAction: false,
|
|
deletionDialogActive: false,
|
|
showFAB: false,
|
|
filterDone: true,
|
|
selection: [],
|
|
selectMode: false,
|
|
recipeList: [],
|
|
// listView: null,
|
|
layout: 1,
|
|
};
|
|
},
|
|
computed: {
|
|
...mapState([
|
|
"sortType",
|
|
"icon",
|
|
"recipes",
|
|
"currentComponent",
|
|
"shakeEnabled",
|
|
]),
|
|
filteredRecipes() {
|
|
let ingredients = this.recipes
|
|
.map((e) => e.ingredients.map((f) => f.item.toLowerCase()).join())
|
|
.join();
|
|
|
|
let vm = this;
|
|
|
|
function getIngredients(e) {
|
|
return e.ingredients
|
|
.map((f) => f.item.toLowerCase())
|
|
.join()
|
|
.includes(vm.searchQuery);
|
|
}
|
|
// if ( this.filterDone ) {
|
|
if (this.filterFavourites) {
|
|
return this.recipes
|
|
.filter(
|
|
(e) =>
|
|
e.isFavorite &&
|
|
(e.title.toLowerCase().includes(this.searchQuery) ||
|
|
getIngredients(e))
|
|
)
|
|
.sort(this.sortFunction);
|
|
} else if (this.filterTrylater) {
|
|
return this.recipes
|
|
.filter(
|
|
(e) =>
|
|
!e.tried &&
|
|
(e.title.toLowerCase().includes(this.searchQuery) ||
|
|
getIngredients(e))
|
|
)
|
|
.sort(this.sortFunction);
|
|
} else if (this.selectedCuisine) {
|
|
return this.recipes
|
|
.filter((e) => {
|
|
return (
|
|
this.recipeFilter(e) &&
|
|
(e.title.toLowerCase().includes(this.searchQuery) ||
|
|
getIngredients(e))
|
|
);
|
|
})
|
|
.sort(this.sortFunction);
|
|
} else {
|
|
return this.recipes
|
|
.filter(
|
|
(e) =>
|
|
e.title.toLowerCase().includes(this.searchQuery) ||
|
|
getIngredients(e)
|
|
)
|
|
.sort(this.sortFunction);
|
|
}
|
|
// } else {
|
|
// return 0;
|
|
// }
|
|
},
|
|
noResultFor() {
|
|
if (this.filterFavourites) return "noRecsInFavs";
|
|
if (this.filterTrylater) return "noRecsInTL";
|
|
if (this.selectedCuisine) return "noRecsInFtr";
|
|
return "noRecs";
|
|
},
|
|
screenWidth() {
|
|
return Screen.mainScreen.widthDIPs;
|
|
},
|
|
imgWidth() {
|
|
return Screen.mainScreen.widthDIPs / 2 - 20;
|
|
},
|
|
},
|
|
methods: {
|
|
...mapActions([
|
|
"setCurrentComponentAction",
|
|
"setSortTypeAction",
|
|
"deleteRecipeAction",
|
|
"deleteRecipesAction",
|
|
"setShakeAction",
|
|
]),
|
|
onPageLoad(args) {
|
|
const page = args.object;
|
|
page.bindingContext = new Observable();
|
|
this.filterFavourites
|
|
? this.setComponent("Favourites")
|
|
: this.filterTrylater
|
|
? this.setComponent("Try Later")
|
|
: this.selectedCuisine
|
|
? this.setComponent("Filtered recipes")
|
|
: this.setComponent("EnRecipes");
|
|
if (!this.selectMode) this.showFAB = true;
|
|
if (this.shakeEnabled) {
|
|
if (utils.hasAccelerometer())
|
|
startAccelerometerUpdates((data) => this.onSensorData(data));
|
|
else this.setShakeAction(false);
|
|
}
|
|
if (this.showSearch || this.selectMode) this.hijackLocalBackEvent();
|
|
this.showDrawer();
|
|
this.closeDrawer();
|
|
},
|
|
onPageUnload() {
|
|
if (this.shakeEnabled) stopAccelerometerUpdates();
|
|
this.releaseGlobalBackEvent();
|
|
this.releaseLocalBackEvent();
|
|
},
|
|
listViewLoad(args) {
|
|
let e = args.object.android;
|
|
e.setSelector(new android.graphics.drawable.StateListDrawable());
|
|
e.setDivider(null);
|
|
e.setDividerHeight(1);
|
|
},
|
|
switchLayout() {
|
|
if (this.layout == 3) this.layout = 1;
|
|
else this.layout++;
|
|
this.$refs.listview.refresh();
|
|
},
|
|
getLayout() {
|
|
switch (this.layout) {
|
|
case 1:
|
|
return "one";
|
|
case 2:
|
|
return "two";
|
|
case 3:
|
|
return "three";
|
|
}
|
|
},
|
|
|
|
// HELPERS
|
|
openSearch() {
|
|
this.showSearch = true;
|
|
this.showFAB = false;
|
|
this.hijackLocalBackEvent();
|
|
},
|
|
closeSearch() {
|
|
this.searchQuery = "";
|
|
Utils.ad.dismissSoftInput();
|
|
this.showSearch = false;
|
|
this.showFAB = true;
|
|
this.releaseLocalBackEvent();
|
|
},
|
|
setComponent(comp) {
|
|
this.setCurrentComponentAction(comp);
|
|
this.hijackGlobalBackEvent();
|
|
},
|
|
clearSearch() {
|
|
this.searchQuery = "";
|
|
},
|
|
updateList(value) {
|
|
clearTimeout(typingTimer);
|
|
typingTimer = setTimeout((e) => {
|
|
this.searchQuery = value;
|
|
}, 750);
|
|
},
|
|
formattedTotalTime(prepTime, cookTime) {
|
|
let t1 = prepTime.split(":");
|
|
let t2 = cookTime.split(":");
|
|
let minutes = parseInt(t1[1]) + parseInt(t2[1]);
|
|
let m = minutes % 60;
|
|
let h = parseInt(t1[0]) + parseInt(t2[0]) + Math.floor(minutes / 60);
|
|
let hr = localize("hr");
|
|
let min = localize("min");
|
|
let mins = h * 60 + m;
|
|
return {
|
|
time: h ? (m ? `${h} ${hr} ${m} ${min}` : `${h} ${hr}`) : `${m} ${min}`,
|
|
duration: `${mins}`,
|
|
};
|
|
},
|
|
randomRecipeID() {
|
|
// TODO: show only from selected filter
|
|
let min = 0;
|
|
let max = this.filteredRecipes.length - 1;
|
|
let randomIndex = Math.round(Math.random() * (max - min));
|
|
return this.filteredRecipes[randomIndex].id;
|
|
},
|
|
recipeFilter(e) {
|
|
let cuisineMatched = e.cuisine === this.selectedCuisine;
|
|
let allCuisines = /allCuis/.test(this.selectedCuisine);
|
|
let categoryMatched = e.category === this.selectedCategory;
|
|
let allCategories = /allCats/.test(this.selectedCategory);
|
|
let tagMatched = e.tags.includes(this.selectedTag);
|
|
let allTags = /allTs/.test(this.selectedTag);
|
|
let cuisine = cuisineMatched || allCuisines;
|
|
|
|
return this.selectedTag && !allTags
|
|
? (categoryMatched || allCategories) && cuisine && tagMatched
|
|
: this.selectedCategory && !allCategories
|
|
? cuisine && categoryMatched
|
|
: cuisine;
|
|
},
|
|
searchAll() {
|
|
this.$emit("backToHome");
|
|
},
|
|
sortFunction(a, b) {
|
|
const titleOrder = a.title
|
|
.toLowerCase()
|
|
.localeCompare(b.title.toLowerCase(), Device.language, {
|
|
ignorePunctuation: true,
|
|
});
|
|
let d1 = this.formattedTotalTime(a.prepTime, a.cookTime).duration;
|
|
let d2 = this.formattedTotalTime(b.prepTime, b.cookTime).duration;
|
|
let ld1 = new Date(a.lastModified);
|
|
let ld2 = new Date(b.lastModified);
|
|
let cd1 = new Date(a.created);
|
|
let cd2 = new Date(b.created);
|
|
let r1 = a.rating;
|
|
let r2 = b.rating;
|
|
|
|
function difficultyLevel(l) {
|
|
switch (l) {
|
|
case "Easy":
|
|
return 1;
|
|
case "Moderate":
|
|
return 2;
|
|
case "Challenging":
|
|
return 3;
|
|
}
|
|
}
|
|
let dl1 = difficultyLevel(a.difficulty);
|
|
let dl2 = difficultyLevel(b.difficulty);
|
|
switch (this.sortType) {
|
|
case "Title":
|
|
return titleOrder > 0 ? 1 : titleOrder < 0 ? -1 : 0;
|
|
break;
|
|
case "Quickest first":
|
|
return d1 > d2 ? 1 : d1 < d2 ? -1 : 0;
|
|
break;
|
|
case "Slowest first":
|
|
return d1 > d2 ? -1 : d1 < d2 ? 1 : 0;
|
|
break;
|
|
case "Rating":
|
|
return r1 > r2 ? -1 : r1 < r2 ? 1 : 0;
|
|
break;
|
|
case "Difficulty level":
|
|
return dl1 > dl2 ? 1 : dl1 < dl2 ? -1 : 0;
|
|
break;
|
|
case "Last updated":
|
|
return ld1 < ld2 ? 1 : ld1 > ld2 ? -1 : 0;
|
|
break;
|
|
case "Newest first":
|
|
return cd1 < cd2 ? 1 : cd1 > cd2 ? -1 : 0;
|
|
break;
|
|
case "Oldest first":
|
|
return cd1 < cd2 ? -1 : cd1 > cd2 ? 1 : 0;
|
|
break;
|
|
}
|
|
},
|
|
isFirstItem(id) {
|
|
let length = this.filteredRecipes.length;
|
|
return id == this.filteredRecipes[0].id
|
|
? "firstItem"
|
|
: id == this.filteredRecipes[length - 1].id
|
|
? "lastItem"
|
|
: "";
|
|
},
|
|
isLastItem(id) {
|
|
let length = this.filteredRecipes.length;
|
|
// let lastIsOdd = ( length - 1 ) % 2 == 0
|
|
// if ( this.filteredRecipes.length > 1 ) {
|
|
// if ( id == this.filteredRecipes[ length - 2 ].id ) {
|
|
// if ( !lastIsOdd ) return 'lastItem'
|
|
// }
|
|
// }
|
|
if (id == this.filteredRecipes[length - 1].id) return "lastItem";
|
|
},
|
|
|
|
// NAVIGATION HANDLERS
|
|
hijackLocalBackEvent() {
|
|
this.releaseGlobalBackEvent();
|
|
AndroidApplication.on(
|
|
AndroidApplication.activityBackPressedEvent,
|
|
this.localBackEvent
|
|
);
|
|
},
|
|
releaseLocalBackEvent() {
|
|
AndroidApplication.off(
|
|
AndroidApplication.activityBackPressedEvent,
|
|
this.localBackEvent
|
|
);
|
|
this.hijackGlobalBackEvent();
|
|
},
|
|
localBackEvent(args) {
|
|
args.cancel = true;
|
|
if (this.selectMode) this.clearSelection();
|
|
this.closeDrawer();
|
|
this.closeSearch();
|
|
},
|
|
addRecipe() {
|
|
this.showFAB = false;
|
|
this.releaseGlobalBackEvent();
|
|
this.$navigateTo(EditRecipe, {
|
|
props: {
|
|
selectedCuisine: this.selectedCuisine,
|
|
selectedCategory: this.selectedCategory,
|
|
selectedTag: this.selectedTag,
|
|
filterFavourites: this.filterFavourites,
|
|
filterTrylater: this.filterTrylater,
|
|
},
|
|
});
|
|
},
|
|
viewRecipe(recipeID) {
|
|
this.showFAB = false;
|
|
this.$navigateTo(ViewRecipe, {
|
|
props: {
|
|
filterTrylater: this.filterTrylater,
|
|
recipeID,
|
|
},
|
|
backstackVisible: false,
|
|
});
|
|
},
|
|
viewRandomRecipe() {
|
|
this.showFAB = false;
|
|
this.$navigateTo(ViewRecipe, {
|
|
props: {
|
|
filterTrylater: false,
|
|
recipeID: this.randomRecipeID(),
|
|
},
|
|
backstackVisible: false,
|
|
});
|
|
},
|
|
|
|
// LIST HANDLERS
|
|
sortDialog() {
|
|
this.releaseGlobalBackEvent();
|
|
this.$showModal(ActionDialog, {
|
|
props: {
|
|
title: "srt",
|
|
list: [
|
|
"Title",
|
|
"Quickest first",
|
|
"Slowest first",
|
|
"Rating",
|
|
"Difficulty level",
|
|
"Last updated",
|
|
"Newest first",
|
|
"Oldest first",
|
|
],
|
|
stretch: false,
|
|
helpIcon: "sort",
|
|
bgColor: "#858585",
|
|
},
|
|
}).then((action) => {
|
|
if (action && action !== "Cancel" && this.sortType !== action) {
|
|
this.setSortTypeAction(action);
|
|
ApplicationSettings.setString("sortType", action);
|
|
this.updateSort();
|
|
}
|
|
this.hijackGlobalBackEvent();
|
|
});
|
|
},
|
|
|
|
// DATA HANDLERS
|
|
addToSelection(args, id) {
|
|
// console.log(args, id);
|
|
this.showFAB = false;
|
|
if (!this.selectMode) this.hijackLocalBackEvent();
|
|
this.selectMode = true;
|
|
this.$emit("selectModeOn", false);
|
|
let item = args.object;
|
|
console.log(item.className);
|
|
let classes = item.className;
|
|
if (classes.includes("selected")) {
|
|
item.className = classes.replace(/selected/g, "");
|
|
this.selection.splice(this.selection.indexOf(id), 1);
|
|
this.recipeList.splice(this.selection.indexOf(id), 1);
|
|
} else {
|
|
item.className = classes + " selected";
|
|
this.selection.push(id);
|
|
this.recipeList.push(item);
|
|
}
|
|
if (!this.selection.length) this.clearSelection();
|
|
},
|
|
clearSelection() {
|
|
this.selectMode = false;
|
|
this.$emit("selectModeOn", true);
|
|
this.selection = [];
|
|
this.recipeList.forEach((e) => {
|
|
e.className = e.className.replace(/selected/g, "");
|
|
});
|
|
this.releaseLocalBackEvent();
|
|
this.showFAB = true;
|
|
},
|
|
deleteSelection() {
|
|
this.selection.length === 1
|
|
? this.deleteRecipe(this.selection[0])
|
|
: this.deleteRecipes(this.selection);
|
|
},
|
|
exportSelection() {},
|
|
deleteRecipe(id) {
|
|
this.deletionDialogActive = true;
|
|
let index = this.recipes.findIndex((e) => e.id === id);
|
|
this.$showModal(ConfirmDialog, {
|
|
props: {
|
|
title: localize("conf"),
|
|
description: `${localize("delRecInfo")} "${
|
|
this.recipes[index].title
|
|
}"`,
|
|
cancelButtonText: "cBtn",
|
|
okButtonText: "dBtn",
|
|
helpIcon: "del",
|
|
bgColor: "#c92a2a",
|
|
},
|
|
}).then((action) => {
|
|
if (action) {
|
|
this.deleteRecipeAction({
|
|
index,
|
|
id,
|
|
});
|
|
if (!this.filteredRecipes.length) this.$emit("backToHome");
|
|
this.clearSelection();
|
|
}
|
|
this.deletionDialogActive = false;
|
|
});
|
|
},
|
|
deleteRecipes(idsArr) {
|
|
this.deletionDialogActive = true;
|
|
this.$showModal(ConfirmDialog, {
|
|
props: {
|
|
title: localize("conf"),
|
|
description: `${localize("delRecsInfo")} ${
|
|
this.selection.length
|
|
} ${localize("recs")}`,
|
|
cancelButtonText: "cBtn",
|
|
okButtonText: "dBtn",
|
|
helpIcon: "del",
|
|
bgColor: "#c92a2a",
|
|
},
|
|
}).then((action) => {
|
|
if (action) {
|
|
this.deleteRecipesAction(idsArr);
|
|
if (!this.filteredRecipes.length) this.$emit("backToHome");
|
|
this.clearSelection();
|
|
}
|
|
this.deletionDialogActive = false;
|
|
});
|
|
},
|
|
|
|
// SHAKE DETECTOR
|
|
onSensorData({ x, y, z }) {
|
|
x = x.toFixed(2);
|
|
y = y.toFixed(2);
|
|
z = z.toFixed(2);
|
|
const FORCE_THRESHOLD = 1;
|
|
const TIME_THRESHOLD = 150;
|
|
const SHAKE_TIMEOUT = 600;
|
|
const SHAKE_THROTTLE = 600;
|
|
const SHAKE_COUNT = 3;
|
|
const now = time();
|
|
if (now - lastForce > SHAKE_TIMEOUT) {
|
|
shakeCount = 0;
|
|
}
|
|
let timeDelta = now - lastTime;
|
|
if (timeDelta > TIME_THRESHOLD) {
|
|
let forceVector = Math.abs(
|
|
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2)) - 1
|
|
);
|
|
if (forceVector > FORCE_THRESHOLD) {
|
|
shakeCount++;
|
|
if (shakeCount >= SHAKE_COUNT && now - lastShake > SHAKE_THROTTLE) {
|
|
lastShake = now;
|
|
shakeCount = 0;
|
|
if (this.filteredRecipes.length) {
|
|
vibrator.vibrate(100);
|
|
this.viewRandomRecipe();
|
|
}
|
|
}
|
|
lastForce = now;
|
|
}
|
|
lastTime = now;
|
|
}
|
|
},
|
|
},
|
|
mounted() {
|
|
this.showFAB = true;
|
|
},
|
|
};
|
|
</script>
|