enrecipes/app/components/MealPlanner.vue
2021-05-31 20:29:15 +05:30

461 lines
12 KiB
Vue

<template>
<Page @loaded="pgLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="*">
<ScrollView
@scroll="!edit && svLoad($event)"
rowSpan="2"
scrollBarIndicatorVisible="false"
>
<StackLayout>
<GridLayout rows="auto" columns="*, auto, 8">
<Label class="pageTitle" :text="'planner' | L" />
<Button
col="1"
class="ico"
:text="icon.cog"
@tap="$navigateTo(MPSettings)"
/>
</GridLayout>
<GridLayout
class="calendar"
columns="*, *, *, *, *, *, *"
rows="auto, auto, auto, auto, auto, auto, auto, auto"
>
<Button class="ico navBtn" :text="icon.left" @tap="prevMonth" />
<Label
class="monthName"
@touch="touchMonthYearPicker"
col="1"
colSpan="5"
:text="$options.filters.L(mNames[month]) + ' ' + year"
/>
<Button
class="ico navBtn"
col="6"
:text="icon.right"
@tap="nextMonth"
/>
<Label
class="dayName"
row="1"
:col="i"
v-for="(d, i) in getDayNames"
:key="d"
:text="d | L"
/>
<Button
class="min day"
:class="{
tb: isToday(d),
activeDay: isActive(d),
hasPlans: hasPlans(d),
}"
:row="getrow(i)"
:col="i % 7"
v-for="(d, i) in getCal"
:key="i"
:text="d ? d : null"
@tap="setToday(d)"
/>
</GridLayout>
<StackLayout class="dayPlan">
<StackLayout
v-for="(mealType, index) in mealTimesWithRecipes"
:key="'mealType' + index"
>
<GridLayout columns="auto, auto">
<Label
class="periodLabel tb"
:class="mealType"
:text="mealType | L"
/>
<Button
:visibility="edit ? 'visible' : 'hidden'"
col="1"
class="ico"
:text="icon.plus"
@tap="addRecipe(mealType)"
/>
</GridLayout>
<GridLayout
:columns="`*, ${edit ? 'auto' : 0}`"
v-for="(recipeID, index) in getRecipes[mealType]"
:key="mealType + index"
>
<Button
class="recipeTitle"
:text="getRecipeTitle(recipeID)"
@tap="viewRecipe(recipeID)"
/>
<Button
:visibility="edit ? 'visible' : 'hidden'"
col="1"
class="ico x"
:text="icon.x"
@tap="removeRecipe(mealType, recipeID)"
/>
</GridLayout>
</StackLayout>
</StackLayout>
</StackLayout>
</ScrollView>
<GridLayout
row="1"
@loaded="abLoad"
class="appbar"
:hidden="showUndo"
columns="auto, *, auto, auto"
>
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
<Button
class="ico"
:text="icon.tod"
:hidden="isExactlyToday"
@tap="goToToday"
col="2"
/>
<Button
class="ico fab"
:text="edit ? icon.done : icon.edit"
@tap="toggleEditMode"
col="3"
/>
</GridLayout>
<SnackBar
:hidden="!showUndo"
:count="countdown"
:msg="snackMsg"
:undo="undoDel"
:action="hideBar"
:onload="sbLoad"
/>
</GridLayout>
</Page>
</template>
<script>
import { Observable, CoreTypes } from "@nativescript/core";
import { mapState, mapActions } from "vuex";
import ViewRecipe from "./ViewRecipe";
import MPSettings from "./settings/MPSettings";
import ActionWithSearch from "./modals/ActionWithSearch";
import MonthYearPicker from "./modals/MonthYearPicker";
import SnackBar from "./sub/SnackBar";
let barTimer;
export default {
components: {
SnackBar,
},
data() {
return {
mealTimes: ["breakfast", "lunch", "dinner", "snacks"],
dNames: ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"],
year: 2021,
mNames: [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
],
month: 0,
date: null,
edit: false,
scrollPos: 1,
appbar: null,
snackbar: null,
countdown: 5,
snackMsg: null,
showUndo: false,
undo: false,
MPSettings: MPSettings,
temp: 0,
};
},
computed: {
...mapState(["icon", "recipes", "mealPlans", "mondayFirst"]),
todaysTime() {
return new Date(this.year, this.month, this.date, 0).getTime();
},
getRecipes() {
if (this.mealPlans.length) {
return this.mealPlans.reduce((acc, e) => {
if (e.date == this.todaysTime) {
acc[e.type] = [...(acc[e.type] || []), e.title];
}
return acc;
}, {});
} else return 0;
},
getDayNames() {
let dNames = [...this.dNames];
if (!this.mondayFirst) dNames.unshift(dNames.pop());
return dNames;
},
getCal() {
let y = this.year;
let m = this.month;
let ds = new Date(y, m + 1, 0).getDate();
let fd = new Date(y, m, 1).getDay();
let ld = new Date(y, m, ds).getDay();
if (this.mondayFirst) fd -= 1;
let days = new Array(fd).fill(0);
// let prevDays = Array.from(
// { length: fd },
// (e, k) => k + new Date(!m ? y - 1 : y, m, 0).getDate() - fd + 1
// );
// let days = prevDays;
for (let i = 1; i <= ds; i++) days.push(i);
// for (let i = 1; i <= 6 - ld; i++) days.push(i);
return days;
},
isExactlyToday() {
let d = new Date();
return (
this.year == d.getFullYear() &&
this.month == d.getMonth() &&
this.date == d.getDate()
);
},
mealTimesWithRecipes() {
return this.mealTimes.filter(
(e) => (this.getRecipes[e] && this.getRecipes[e].length) || this.edit
);
},
},
methods: {
...mapActions([
"setComponent",
"addMealPlanAction",
"deleteMealPlanAction",
]),
pgLoad({ object }) {
object.bindingContext = new Observable();
this.setComponent("MealPlanner");
if (!this.date || this.date === new Date().getDate()) this.goToToday();
},
abLoad({ object }) {
this.appbar = object;
},
sbLoad({ object }) {
this.snackbar = object;
},
svLoad(args) {
let scrollUp;
let y = args.scrollY;
if (y) {
scrollUp = y < this.scrollPos;
this.scrollPos = Math.abs(y);
let ab = this.appbar.translateY;
if (!scrollUp && ab == 0) {
this.appbar.animate({
translate: { x: 0, y: 64 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
});
} else if (scrollUp && ab == 64) {
this.appbar.animate({
translate: { x: 0, y: 0 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
});
}
}
},
// HELPERS
showAppBar() {
this.appbar.translateY = 0;
},
getrow(i) {
return Math.floor(2 + i / 7);
},
getDate(index) {
let date = new Date();
date.setDate(date.getDate() + index);
return date.getTime();
},
getRecipeTitle(id) {
let recipe = this.recipes.filter((e) => e.id === id)[0];
return recipe ? recipe.title : `[ ${this.$options.filters.L("resNF")} ]`;
},
// NAVIGATION HANDLERS
viewRecipe(recipeID) {
let recipe = this.recipes.filter((e) => e.id === recipeID)[0];
if (recipe) {
this.$navigateTo(ViewRecipe, {
props: {
filterTrylater: true,
recipeID,
},
});
}
},
// CALENDAR
prevMonth() {
if (this.month == 0) {
this.year--;
this.month = 11;
} else this.month--;
this.showAppBar();
},
nextMonth() {
if (this.month == 11) {
this.year++;
this.month = 0;
} else this.month++;
this.showAppBar();
},
goToToday() {
let d = new Date();
this.year = d.getFullYear();
this.month = d.getMonth();
this.date = d.getDate();
this.showAppBar();
},
isToday(date) {
let d = new Date();
return (
this.year == d.getFullYear() &&
this.month == d.getMonth() &&
date == d.getDate()
);
},
isActive(date) {
return this.date == date;
},
hasPlans(date) {
let d = new Date(this.year, this.month, date, 0).getTime();
return this.mealPlans.filter((e) => e.date == d).length;
},
setToday(date) {
if (date) this.date = date;
},
toggleEditMode() {
this.edit = !this.edit;
},
openMonthYearPicker() {
this.$showModal(MonthYearPicker, {
props: {
title: "gtD",
monthNames: this.mNames,
currentM: this.month,
currentY: this.year,
},
}).then((res) => {
if (res) {
this.month = res.month;
this.year = res.year;
}
});
},
// DATA HANDLERS
newMealPlan({ date, type, title, index, inDB }) {
this.addMealPlanAction({
date: date ? date : this.todaysTime,
type,
title,
index,
inDB,
});
},
addRecipe(type) {
let filteredRecipes = this.recipes.filter((e) =>
this.getRecipes[type] ? !this.getRecipes[type].includes(e.id) : true
);
this.$showModal(ActionWithSearch, {
props: {
title: "selRec",
recipes: filteredRecipes,
},
}).then(
(title) =>
title &&
this.newMealPlan({ date: 0, type, title, index: null, inDB: true })
);
},
deleteTempFromDB() {
if (this.temp) {
this.temp.inDB = 1;
this.deleteMealPlanAction(this.temp);
this.temp = 0;
}
},
removeRecipe(type, title) {
this.deleteTempFromDB();
let date = this.todaysTime;
let index = this.mealPlans.findIndex(
(e) => e.date == date && e.type == type && e.title == title
);
let mealPlan = {
date,
type,
title,
index,
};
let temp;
this.temp = temp = mealPlan;
this.deleteMealPlanAction(mealPlan);
this.showUndoBar("recRm")
.then(() => this.newMealPlan({ date, type, title, index }))
.catch(() => {
temp.inDB = 1;
console.log("deleting inDB after catch: ", temp);
this.deleteMealPlanAction(temp);
});
},
showUndoBar(message) {
return new Promise((resolve, reject) => {
this.animateBar(this.appbar, 0).then(() => {
this.showUndo = true;
this.snackMsg = message;
this.countdown = 5;
this.animateBar(this.snackbar, 1).then(() => {
let a = 5;
clearInterval(barTimer);
barTimer = setInterval(() => {
if (this.undo) {
this.hideBar();
resolve(true);
}
this.countdown = Math.round((a -= 0.1));
if (this.countdown < 1) {
this.hideBar();
reject(true);
}
}, 100);
});
});
});
},
hideBar() {
clearInterval(barTimer);
this.animateBar(this.snackbar, 0).then(() => {
this.showUndo = this.undo = false;
this.animateBar(this.appbar, 1);
});
},
undoDel() {
this.undo = true;
},
//HELPERS
touchMonthYearPicker({ object, action }) {
object.className = action.match(/down|move/)
? "monthName fade"
: "monthName";
if (action == "up") this.openMonthYearPicker();
},
},
};
</script>