enrecipes/app/components/EnRecipes.vue
2021-06-18 18:22:03 +05:30

1132 lines
33 KiB
Vue

<template>
<Page @loaded="pgLoad" @unloaded="pgUnload" actionBarHidden="true">
<GridLayout rows="*, 104, auto" columns="*">
<CollectionView
rowSpan="4"
:spanSize="getSpanSize"
for="recipe in getRecipes"
@loaded="cvLoad"
:itemTemplateSelector="getLayout"
:colWidth="layout == 'grid' || layout == 'photogrid' ? '50%' : '100%'"
@scroll="!selectMode && cvScroll($event)"
>
<v-template name="header">
<RGridLayout :rtl="RTL" rows="auto" columns="*, auto, 12">
<RLabel class="pTitle tw tb" :text="`${currentComp}` | L" />
<Button
col="1"
class="ico"
:text="icon.cog"
@tap="navigateTo(AppSettings, 'Settings', 1)"
/>
</RGridLayout>
</v-template>
<v-template name="lists">
<RStackLayout :rtl="RTL" orientation="horizontal" padding="0 16 24">
<GridLayout
rows="32"
columns="32, auto"
class="segment rtl"
v-for="(item, index) in topmenu"
:key="index"
:class="{
select: currentComp == item.title,
}"
@touch="touchSelector($event, item.title, item.title)"
>
<Label class="ico tc vc" :text="icon[item.icon]" />
<Label
col="1"
class="v vc"
:class="{ f: RTL }"
:hidden="!getRecipeCount(item.title)"
:text="getRecipeCount(item.title)"
/>
</GridLayout>
<GridLayout
:hidden="currentComp !== 'Filtered recipes'"
rows="32"
columns="32, auto"
class="segment rtl"
:class="{
select: currentComp === 'Filtered recipes',
}"
>
<Label class="ico tc vc" :text="icon.filter" />
<Label
col="1"
class="v vc"
:class="{ f: RTL }"
:text="getRecipeCount('filtered')"
/>
</GridLayout>
</RStackLayout>
</v-template>
<v-template name="detailed">
<RGridLayout
:rtl="RTL"
class="recipe"
:class="getItemPos(recipe.id)"
rows="auto"
columns="96, *"
@longPress="
selectMode ? viewRecipe(recipe.id) : addToSelection(recipe.id)
"
@touch="touchRecipe"
@tap="
selectMode ? addToSelection(recipe.id) : viewRecipe(recipe.id)
"
>
<Label
:hidden="recipe.image"
verticalAlignment="center"
class="ico imgHolder"
@loaded="centerLabel"
width="96"
height="96"
fontSize="48"
:text="icon.img"
/>
<Image
class="imgHolder"
verticalAlignment="center"
:hidden="!recipe.image"
:src="recipe.image"
stretch="none"
decodeWidth="96"
decodeHeight="96"
loadMode="async"
/>
<StackLayout class="info vc" col="1">
<RLabel :text="recipe.title" class="tb title tw" />
<RStackLayout :rtl="RTL" class="oh"
><Label class="ico s rtl vc" :text="icon.cuisine" />
<Label class="attr" :text="recipe.cuisine | L" />
<Label class="ico s vc" :text="icon.category" />
<Label class="attr" :text="recipe.category | L" />
</RStackLayout>
<RStackLayout :rtl="RTL" :hidden="!recipe.tags.length" class="oh">
<Label class="ico s rtl vc" :text="icon.tag" />
<Label class="attr" :text="getTags(recipe.tags)" />
</RStackLayout>
<RStackLayout :rtl="RTL" class="oh">
<Label class="ico s vc" :text="icon.star" />
<Label class="attr" :text="getLocaleN(recipe.rating)" />
<Label class="ico s vc" :text="icon.time" />
<Label
class="attr"
:text="`${totalTime(recipe.prepTime, recipe.cookTime).time}`"
/>
<Label class="ico s vc" :text="icon.diff" />
<Label class="attr" :text="recipe.difficulty | L" />
</RStackLayout>
</StackLayout>
</RGridLayout>
</v-template>
<v-template name="grid">
<GridLayout
class="recipe grid"
:class="getItemPos(recipe.id)"
rows="auto, auto"
columns="*"
@longPress="
selectMode ? viewRecipe(recipe.id) : addToSelection(recipe.id)
"
@touch="touchRecipe"
@tap="
selectMode ? addToSelection(recipe.id) : viewRecipe(recipe.id)
"
>
<Image
class="imgHolder"
v-if="recipe.image"
:src="recipe.image"
stretch="aspectFit"
:decodeWidth="imgWidth"
:decodeHeight="imgWidth"
loadMode="async"
/>
<Label
v-else
width="100%"
:height="imgWidth"
@loaded="centerLabel"
class="ico imgHolder"
:fontSize="imgWidth / 2"
:text="icon.img"
/>
<StackLayout class="info" row="1">
<RLabel :text="recipe.title" class="tb title tw" />
<FlexboxLayout
flexWrap="wrap"
:justifyContent="RTL ? 'flex-end' : 'flex-start'"
>
<RStackLayout :rtl="RTL" class="oh">
<Label class="ico s rtl vc" :text="icon.cuisine" />
<Label class="attr" :text="recipe.cuisine | L" />
</RStackLayout>
<RStackLayout :rtl="RTL" class="oh">
<Label class="ico s vc" :text="icon.category" />
<Label class="attr" :text="recipe.category | L" />
</RStackLayout>
</FlexboxLayout>
<RStackLayout :rtl="RTL" :hidden="!recipe.tags.length" class="oh">
<Label class="ico s rtl vc" :text="icon.tag" />
<Label class="attr" :text="getTags(recipe.tags)" />
</RStackLayout>
</StackLayout>
</GridLayout>
</v-template>
<v-template name="photogrid">
<RGridLayout
:rtl="RTL"
class="recipe grid photogrid"
:class="getItemPos(recipe.id)"
rows="auto, auto"
columns="*"
@longPress="
selectMode ? viewRecipe(recipe.id) : addToSelection(recipe.id)
"
@touch="touchRecipe"
@tap="
selectMode ? addToSelection(recipe.id) : viewRecipe(recipe.id)
"
>
<Image
class="imgHolder"
v-if="recipe.image"
:src="recipe.image"
stretch="aspectFit"
:decodeWidth="imgWidth"
:decodeHeight="imgWidth"
loadMode="async"
/>
<Label
v-else
width="100%"
:height="imgWidth"
@loaded="centerLabel"
class="ico imgHolder"
:fontSize="imgWidth / 2"
:text="icon.img"
/>
<StackLayout class="info" row="1">
<RLabel :text="recipe.title" class="tb title tw" />
</StackLayout>
</RGridLayout>
</v-template>
<v-template name="simple">
<RGridLayout
:rtl="RTL"
class="recipe simple"
:class="getItemPos(recipe.id)"
columns="*"
@longPress="
selectMode ? viewRecipe(recipe.id) : addToSelection(recipe.id)
"
@touch="touchRecipe"
@tap="
selectMode ? addToSelection(recipe.id) : viewRecipe(recipe.id)
"
>
<StackLayout class="info">
<RLabel :text="recipe.title" class="tb title tw" />
<RStackLayout :rtl="RTL" class="oh">
<Label class="ico s rtl vc" :text="icon.cuisine" />
<Label class="attr" :text="recipe.cuisine | L" />
<Label class="ico s vc" :text="icon.category" />
<Label class="attr" :text="recipe.category | L" />
</RStackLayout>
<RStackLayout :rtl="RTL" :hidden="!recipe.tags.length" class="oh">
<Label class="ico s rtl vc" :text="icon.tag" />
<Label class="attr" :text="getTags(recipe.tags)" />
</RStackLayout>
</StackLayout>
</RGridLayout>
</v-template>
<v-template name="minimal">
<GridLayout
class="recipe simple minimal"
:class="getItemPos(recipe.id)"
columns="*"
@longPress="
selectMode ? viewRecipe(recipe.id) : addToSelection(recipe.id)
"
@touch="touchRecipe"
@tap="
selectMode ? addToSelection(recipe.id) : viewRecipe(recipe.id)
"
>
<StackLayout class="info">
<RLabel :text="recipe.title" class="tb title tw" />
</StackLayout>
</GridLayout>
</v-template>
</CollectionView>
<GridLayout
rowSpan="2"
class="empty"
:hidden="!empty"
rows="*, auto, auto"
columns="*"
>
<RLabel row="1" class="tb t3 tw" :text="empty.title | L" />
<Button
row="2"
v-if="
empty.action && (filterFavourites || filterTrylater || selCuisine)
"
class="text tb big fb"
@loaded="setGravity"
:text="empty.sub | L"
@tap="empty.action"
/>
<RLabel
class="tw"
row="2"
v-else-if="!empty.action"
:text="empty.sub | L"
/>
</GridLayout>
<GridLayout
row="1"
@loaded="tbLoad"
:rows="tbRows"
columns="auto"
class="appbar toolbar sidebar hal"
:class="{ r: RTL }"
:visibility="showTools ? 'visible' : 'hidden'"
>
<RStackLayout
v-for="(item, i) in tbItems"
:key="i"
:row="i"
:rtl="RTL"
class="tool"
@touch="touchTool($event, item.comp, item.title)"
>
<Label class="ico vc" :text="icon[item.icon]" />
<Label col="1" class="v vc" :text="item.title | L" />
</RStackLayout>
</GridLayout>
<RGridLayout
row="2"
@loaded="abLoad"
:rtl="RTL"
class="appbar"
columns="auto, *, auto, auto, auto, auto"
@swipe="stSwipe"
@touch="() => null"
>
<Button
accessibilityLabel="Accessible Label"
accessibilityHint="Just a label"
accessibilityValue="Accessible Label"
class="ico rtl"
@tap="
showSearch
? closeSearch()
: selectMode
? clearSelection()
: toggleTools()
"
:text="
showSearch
? icon.back
: selectMode || showTools
? icon.x
: icon.menu
"
/>
<TextField
id="search"
:class="{ f: RTL }"
@loaded="focusField"
autocapitalizationType="words"
autocorrect="true"
v-if="showSearch"
col="1"
colSpan="5"
:hint="'ser' | L"
@textChange="updateList($event.value)"
/>
<Label
:hidden="!selectMode"
class="tb tw vc lh4"
:text="`${selection.length} ${$options.filters.L('sltd')}`"
col="1"
/>
<StackLayout
class="rtl"
col="2"
colSpan="3"
orientation="horizontal"
:hidden="!recipes.length || selectMode || showSearch"
>
<Button
class="ico"
:class="{ f: RTL }"
:text="selectMode ? icon.exp : icon.sear"
@tap="selectMode ? exportSelection() : openSearch()"
/>
<Button
class="ico"
:class="{ f: RTL }"
:text="icon.sort"
@tap="openSort"
/>
<Button class="ico" :text="icon.filter" @tap="openFilter" />
</StackLayout>
<Button
:hidden="showSearch || selectMode"
class="ico fab"
:text="icon.plus"
col="5"
@tap="addRecipe"
/>
<Button
:hidden="!selectMode"
class="ico"
:text="icon.del"
col="5"
@tap="deleteSelection"
/>
</RGridLayout>
</GridLayout>
</Page>
</template>
<script lang="ts">
import {
ApplicationSettings,
AndroidApplication,
Utils,
Observable,
Device,
Screen,
CoreTypes,
} from "@nativescript/core";
import { localize } from "@nativescript/localize";
import {
startAccelerometerUpdates,
stopAccelerometerUpdates,
} from "@triniwiz/nativescript-accelerometer";
import { mapActions, mapState } from "vuex";
import ViewRecipe from "./ViewRecipe.vue";
import EditRecipe from "./EditRecipe.vue";
import MealPlanner from "./MealPlanner.vue";
import CookingTimer from "./CookingTimer.vue";
// import GroceryList from "./GroceryList.vue";
import AppSettings from "./settings/AppSettings.vue";
import Action from "./modals/Action.vue";
import Confirm from "./modals/Confirm.vue";
import Filters from "./modals/Filter.vue";
import * as utils from "~/shared/utils";
let lastTime = 0;
let lastShake = 0;
let lastForce = 0;
let shakeCount = 0;
let typingTimer;
export default {
data() {
return {
searchQuery: "",
showSearch: 0,
deletionDialogActive: 0,
selection: [],
selectMode: 0,
listview: null,
appbar: null,
toolbar: null,
scrollPos: 1,
filterFavourites: 0,
filterTrylater: 0,
AppSettings: AppSettings,
MealPlanner: MealPlanner,
CookingTimer: CookingTimer,
// GroceryList: GroceryList,
topmenu: [
{
title: "EnRecipes",
icon: "home",
},
{
title: "trylater",
icon: "try",
},
{
title: "favourites",
icon: "fav",
},
],
showTools: 0,
currentComp: "EnRecipes",
};
},
computed: {
...mapState([
"icon",
"sortType",
"recipes",
"cuisines",
"categories",
"yieldUnits",
"mealPlans",
"shake",
"layout",
"selCuisine",
"selCategory",
"selTag",
"timerSound",
"RTL",
]),
filteredRecipes() {
let vm = this;
function getIngredients(e) {
return e.ingredients
.map((f) => f.item.toLowerCase())
.join()
.includes(vm.searchQuery);
}
if (this.filterFavourites) {
return this.recipes
.filter(
(e) =>
e.favorite &&
(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.selCuisine) {
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);
}
},
getRecipes() {
return [{}, {}].concat(this.filteredRecipes);
},
tbItems() {
return [
{
title: "timer",
icon: "timer",
comp: CookingTimer,
},
{
title: "planner",
icon: "cal",
comp: MealPlanner,
},
];
},
tbRows() {
return "48, ".repeat(this.tbItems.length) + 48;
},
noResultFor() {
if (this.filterFavourites || this.filterTrylater || this.selCuisine)
return "noRecsInL";
return "noRecs";
},
imgWidth() {
return Screen.mainScreen.widthDIPs / 2 - 24;
},
empty() {
let rl = this.recipes.length;
let fr = this.filteredRecipes.length;
let ff = this.filterFavourites;
let ftl = this.filterTrylater;
let sq = this.searchQuery;
interface EmptyState {
title: string;
sub: string;
action?: Function;
}
let r = <EmptyState>{};
if (!rl && !ff && !ftl) {
r.title = "strAdd";
r.sub = "plsAdd";
} else if (!fr && ftl && !sq) {
r.title = "aD";
r.sub = "tLInfo";
} else if (!fr && ff && !sq) {
r.title = "noFavs";
r.sub = "fsList";
} else if (!fr && sq) {
r.title = this.noResultFor;
r.sub = "trySer";
r.action = this.goToHome;
}
return Object.keys(r).length ? r : 0;
},
},
methods: {
...mapActions([
"initListItems",
"initRecipes",
"initMealPlans",
"initTimerPresets",
"setShake",
"setFirstDay",
"setLayout",
"setSortType",
"deleteRecipes",
"clearFilter",
"setTheme",
"setTimerSound",
]),
setComp(comp) {
this.currentComp = comp;
},
pgLoad({ object }) {
object.bindingContext = new Observable();
this.filterFavourites
? this.setComp("favourites")
: this.filterTrylater
? this.setComp("trylater")
: this.selCuisine
? this.setComp("Filtered recipes")
: this.setComp("EnRecipes");
if (this.shake) {
if (utils.hasAccelerometer())
startAccelerometerUpdates((data) => this.onSensorData(data));
else this.setShake(0);
}
this.hijackBackEvent();
setTimeout(() => {
if (this.listview) this.listview.refresh();
}, 1000);
},
pgUnload() {
if (this.shake) stopAccelerometerUpdates();
},
abLoad({ object }) {
this.appbar = object;
},
tbLoad({ object }) {
this.toolbar = object;
},
// Collectionview
cvLoad({ object }) {
const View = android.view.View;
object.android.setOverScrollMode(View.OVER_SCROLL_NEVER);
this.listview = object;
},
cvScroll(args) {
let scrollUp;
let y = args.object.scrollOffset;
if (y) {
scrollUp = y < this.scrollPos;
this.scrollPos = Math.abs(y);
let ab = this.appbar.translateY;
if (!scrollUp && ab == 0) this.hideBars();
else if (scrollUp && ab == 64) this.showBars();
}
},
showBars() {
this.animateBar(this.appbar, 1);
},
hideBars() {
this.showTools && this.toggleTools();
this.animateBar(this.appbar, 0);
},
getSpanSize(index) {
return (this.layout == "grid" || this.layout == "photogrid") &&
(index == 0 || index == 1)
? 2
: 1;
},
getLayout(args, index, items) {
return index == 0 ? "header" : index == 1 ? "lists" : this.layout;
},
// Search
openSearch() {
this.showTools && this.toggleTools();
this.showSearch = 1;
},
closeSearch() {
this.searchQuery = "";
Utils.ad.dismissSoftInput();
this.showSearch = 0;
},
// Sort
openSort() {
this.showTools && this.toggleTools();
this.releaseBackEvent();
this.$showModal(Action, {
props: {
title: "srt",
list: [
"random",
"title",
"Rating",
"Quickest first",
"Slowest first",
"Difficulty level",
"Last updated",
"Newest first",
"Oldest first",
],
selected: this.sortType,
},
}).then((action) => {
if (action && this.sortType !== action) {
this.setSortType(action);
ApplicationSettings.setString("sortType", action);
this.updateSort();
}
this.hijackBackEvent();
});
},
// Filter
openFilter() {
this.setComp("EnRecipes");
this.filterFavourites = this.filterTrylater = 0;
this.showTools && this.toggleTools();
this.releaseBackEvent();
this.$showModal(Filters).then((res) => {
this.setComp(res ? "EnRecipes" : "Filtered recipes");
this.hijackBackEvent();
});
},
// Tools
toggleTools() {
if (this.showTools) {
this.toolbar
.animate({
height: 0,
translate: { x: 0, y: 48 },
duration: 200,
curve: CoreTypes.AnimationCurve.easeIn,
})
.then(() => (this.showTools = 0));
} else {
this.toolbar.height = 1;
this.showTools = 1;
setTimeout(() => {
this.toolbar.animate({
height: 104,
duration: 200,
translate: { x: 0, y: 0 },
curve: CoreTypes.AnimationCurve.easeOut,
});
}, 1);
}
},
// ListHandlers
addToSelection(id) {
this.showTools && this.toggleTools();
this.selectMode = 1;
this.appbar.translateY = 0;
this.selection.includes(id)
? this.selection.splice(this.selection.indexOf(id), 1)
: this.selection.push(id);
this.selection.length ? this.listview.refresh() : this.clearSelection();
},
clearSelection() {
this.selection = [];
this.selectMode = 0;
this.listview.refresh();
},
deleteSelection() {
this.deletionDialogActive = 1;
let hasMany = this.selection.length > 1;
let what = hasMany
? `${this.selection.length} ${localize("recs")}`
: `"${
this.recipes[
this.recipes.findIndex((e) => e.id === this.selection[0])
].title
}"`;
this.$showModal(Confirm, {
props: {
title: localize("conf"),
description: `${localize(
hasMany ? "delRecsInfo" : "delRecInfo",
what
)}`,
cancelButtonText: "cBtn",
okButtonText: "dBtn",
},
}).then((action) => {
if (action) {
this.deleteRecipes(this.selection);
if (!this.filteredRecipes.length) this.goToHome();
this.clearSelection();
}
this.deletionDialogActive = 0;
});
},
exportSelection() {},
// ShakeDetector
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 = Date.now();
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) {
utils.vibrate(100);
this.viewRandomRecipe();
}
}
lastForce = now;
}
lastTime = now;
}
},
// Helpers
getRecipeCount(arg) {
let count = 0;
let a = this.selCuisine;
let b = this.selCategory;
let c = this.selTag;
let cuisine = a && a != "allCuis";
let category = b && b != "allCats";
let tag = c && c != "allTs";
let allCuisines = a && a == "allCuis";
let allCategories = b && b == "allCats";
let allTags = c && c == "allTs";
switch (arg) {
case "EnRecipes":
count = this.recipes.length;
break;
case "trylater":
count = this.recipes.filter((e) => !e.tried).length;
break;
case "favourites":
count = this.recipes.filter((e) => e.favorite).length;
break;
default:
count = this.recipes.filter((e) => {
let cui = e.cuisine === a;
let cat = e.category === b;
let t = e.tags.includes(c);
return allCuisines
? allCategories
? tag
? t
: 1
: category
? allTags
? cat
: tag
? cat && t
: cat
: 1
: cuisine
? allCategories
? allTags
? cui
: tag
? cui && t
: cui
: category
? allTags
? cui && cat
: tag
? cui && cat && t
: cui && cat
: cui
: 0;
}).length;
break;
}
return count && this.getLocaleN(count);
},
centerLabel({ object }) {
object.android.setGravity(17);
},
focusField({ object }) {
if (this.RTL) object.android.setGravity(5);
setTimeout((e) => {
object.focus();
setTimeout((e) => Utils.ad.showSoftInput(object.android), 100);
}, 100);
},
updateList(value) {
clearInterval(typingTimer);
typingTimer = setTimeout(() => {
value = value.replace(/\s+$/, "");
this.searchQuery = value.toLowerCase();
}, 750);
},
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.selCuisine;
let allCuisines = /allCuis/.test(this.selCuisine);
let categoryMatched = e.category === this.selCategory;
let allCategories = /allCats/.test(this.selCategory);
let tagMatched = e.tags.includes(this.selTag);
let allTags = /allTs/.test(this.selTag);
let cuisine = cuisineMatched || allCuisines;
return this.selTag && !allTags
? (categoryMatched || allCategories) && cuisine && tagMatched
: this.selCategory && !allCategories
? cuisine && categoryMatched
: cuisine;
},
sortFunction(a, b) {
const titleOrder = a.title
.toLowerCase()
.localeCompare(b.title.toLowerCase(), Device.language, {
ignorePunctuation: true,
});
let d1 = this.totalTime(a.prepTime, a.cookTime).duration;
let d2 = this.totalTime(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 "random":
return 0.5 - Math.random();
case "title":
return titleOrder > 0 ? 1 : titleOrder < 0 ? -1 : 0;
case "Quickest first":
return d1 > d2 ? 1 : d1 < d2 ? -1 : 0;
case "Slowest first":
return d1 > d2 ? -1 : d1 < d2 ? 1 : 0;
case "Rating":
return r1 > r2 ? -1 : r1 < r2 ? 1 : 0;
case "Difficulty level":
return dl1 > dl2 ? 1 : dl1 < dl2 ? -1 : 0;
case "Last updated":
return ld1 < ld2 ? 1 : ld1 > ld2 ? -1 : 0;
case "Newest first":
return cd1 < cd2 ? 1 : cd1 > cd2 ? -1 : 0;
case "Oldest first":
return cd1 < cd2 ? -1 : cd1 > cd2 ? 1 : 0;
}
},
getItemPos(id) {
let length = this.filteredRecipes.length;
let l2 = this.layout == "grid" || this.layout == "photogrid";
let oddOrEven = this.oddOrEven(id);
let Rsys = utils.sysRTL();
let itemPos =
id == this.filteredRecipes[0].id ||
(length > 1 && l2 && id == this.filteredRecipes[1].id)
? "firstItem"
: id == this.filteredRecipes[length - 1].id ||
(length > 1 &&
l2 &&
oddOrEven == (Rsys ? " even" : " odd") &&
id == this.filteredRecipes[length - 2].id)
? "lastItem"
: "";
let selection = this.selection.includes(id) ? "selected" : "unselected";
let classes = itemPos + " " + selection;
return l2 ? classes + oddOrEven : classes;
},
oddOrEven(id) {
let c = this.filteredRecipes.findIndex((e) => e.id == id) % 2 === 0;
if (utils.sysRTL()) c = !c;
return c ? " odd" : " even";
},
getTags(tags) {
return tags.join(" · ");
},
// NavigationHandlers
hijackBackEvent() {
AndroidApplication.on(
AndroidApplication.activityBackPressedEvent,
this.backEvent
);
},
releaseBackEvent() {
AndroidApplication.off(
AndroidApplication.activityBackPressedEvent,
this.backEvent
);
},
backEvent(args) {
if (this.showSearch) {
args.cancel = true;
this.closeSearch();
} else if (this.selectMode) {
args.cancel = true;
this.clearSelection();
} else if (
["favourites", "trylater", "Filtered recipes"].includes(
this.currentComp
)
) {
args.cancel = true;
this.goToHome();
}
},
goToHome() {
this.setComp("EnRecipes");
this.filterFavourites = this.filterTrylater = null;
this.clearFilter();
},
navigateTo(to, title, page) {
this.showTools && this.toggleTools();
if (page) {
if (/Settings/.test(title))
this.$navigateTo(to, {
transition: {
name: this.RTL ? "slideRight" : "slide",
duration: 200,
curve: "easeOut",
},
});
else
this.$navigateTo(to, {
animated: false,
});
} else if (title !== this.currentComp) {
this.showBars();
this.setComp(title);
this.filterFavourites = to == "favourites";
this.filterTrylater = to == "trylater";
this.clearFilter();
}
},
stSwipe({ direction }) {
const comps = ["EnRecipes", "trylater", "favourites", "Filtered recipes"];
let index = comps.findIndex((e) => e == this.currentComp);
switch (direction) {
case 1:
if (index > 0) {
this.showBars();
this.navigateTo(comps[index - 1], comps[index - 1]);
this.setComp(comps[index - 1]);
this.filterFavourites = comps[index - 1] == "favourites";
this.filterTrylater = comps[index - 1] == "trylater";
this.clearFilter();
}
break;
case 2:
if (index <= 1) {
this.showBars();
this.navigateTo(comps[index + 1], comps[index + 1]);
this.setComp(comps[index + 1]);
this.filterFavourites = comps[index + 1] == "favourites";
this.filterTrylater = comps[index + 1] == "trylater";
}
break;
}
},
addRecipe() {
this.showTools && this.toggleTools();
this.$navigateTo(EditRecipe, {
props: {
filterFavourites: this.filterFavourites,
filterTrylater: this.filterTrylater,
},
animated: false,
});
},
viewRecipe(recipeID) {
this.showTools && this.toggleTools();
this.$navigateTo(ViewRecipe, {
props: {
filterTrylater: this.filterTrylater,
recipeID,
},
animated: false,
});
},
viewRandomRecipe() {
this.showTools && this.toggleTools();
this.$navigateTo(ViewRecipe, {
props: {
filterTrylater: 1,
recipeID: this.randomRecipeID(),
},
animated: false,
});
},
touchSelector({ object, action }, comp, title) {
if (this.currentComp != title) {
this.touchFade(object, action);
if (action == "up") this.navigateTo(comp, title);
}
},
touchRecipe({ object, action }) {
if (!this.selectMode) this.touchFade(object, action);
},
touchTool({ object, action }, comp, value) {
this.touchFade(object, action);
if (action == "up") this.navigateTo(comp, value, 1);
},
},
};
</script>