enrecipes/app/store.ts
2021-04-23 20:00:50 +05:30

822 lines
22 KiB
TypeScript

import Vue from 'nativescript-vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import { CouchBase } from '@triniwiz/nativescript-couchbase'
import { getFileAccess, File, ApplicationSettings, Device } from '@nativescript/core'
const EnRecipesDB = new CouchBase('EnRecipes')
const userCuisinesDB = new CouchBase('userCuisines')
const userCategoriesDB = new CouchBase('userCategories')
const userYieldUnitsDB = new CouchBase('userYieldUnits')
const userUnitsDB = new CouchBase('userUnits')
const mealPlansDB = new CouchBase('mealPlans')
const defaultCuisines = [
'American',
'Brazilian',
'British',
'Chinese',
'Danish',
'Egyptian',
'Filipino',
'French',
'German',
'Greek',
'Indian',
'Irish',
'Italian',
'Jamaican',
'Japanese',
'Jewish',
'Kenyan',
'Korean',
'Mexican',
'Nigerian',
'Portuguese',
'Russian',
'Scottish',
'Spanish',
'Sri Lankan',
'Swedish',
'Thai',
'Turkish',
'Vietnamese',
]
const defaultCategories = [
'Appetizers',
'Barbecue',
'Beverages',
'Breads',
'breakfast',
'Desserts',
'dinner',
'Healthy',
'lunch',
'Main dishes',
'Meat',
'Noodles',
'Pasta',
'Poultry',
'Rice',
'Salads',
'Sauces',
'Seafood',
'Side dishes',
'snacks',
'Soups',
'Undefined',
'Vegan',
'Vegetarian',
]
const defaultYieldUnits = [
'Serving',
'Piece',
'Teaspoon',
'Tablespoon',
'Fluid Ounce',
'Ounce',
'Pound',
'Gram',
'Kilogram',
'Cup',
'Gallon',
'Millilitre',
'Litre',
'Roll',
'Patty',
'Loaf',
]
const defaultUnits = [
'unit',
'tsp',
'dsp',
'tbsp',
'fl oz',
'cup',
'pt',
'qt',
'gal',
'ml',
'l',
'oz',
'lb',
'mg',
'g',
'kg',
'cm',
'in',
'leaf',
'clove',
'piece',
'pinch',
'drop',
'dozen',
'stick',
'small',
'medium',
'large',
]
const listItems = {
cuisines: {
db: userCuisinesDB,
key: 'userCuisines',
stateName: 'cuisines',
sort: true,
defaultItems: defaultCuisines,
},
categories: {
db: userCategoriesDB,
key: 'userCategories',
stateName: 'categories',
sort: true,
defaultItems: defaultCategories,
},
yieldUnits: {
db: userYieldUnitsDB,
key: 'userYieldUnits',
stateName: 'yieldUnits',
sort: false,
defaultItems: defaultYieldUnits,
},
units: {
db: userUnitsDB,
key: 'userUnits',
stateName: 'units',
sort: false,
defaultItems: defaultUnits,
},
}
export default new Vuex.Store({
state: {
recipes: [],
cuisines: [],
categories: [],
yieldUnits: [],
units: [],
mealPlans: [],
icon: {
back: '\ue900',
bag: '\ue901',
bagged: '\ue902',
cal: '\ue903',
category: '\ue904',
check: '\ue905',
cog: '\ue906',
cuisine: '\ue907',
db: '\ue908',
del: '\ue909',
diff: '\ue90a',
don: '\ue90b',
done: '\ue90c',
edit: '\ue90d',
exp: '\ue90e',
fav: '\ue90f',
faved: '\ue910',
filter: '\ue911',
folder: '\ue912',
gh: '\ue913',
help: '\ue914',
home: '\ue915',
img: '\ue916',
imp: '\ue917',
info: '\ue918',
interface: '\ue919',
lang: '\ue91a',
layout: '\ue91b',
left: '\ue91c',
menu: '\ue91d',
opts: '\ue91e',
plus: '\ue91f',
price: '\ue920',
priv: '\ue921',
reset: '\ue922',
right: '\ue923',
save: '\ue924',
sear: '\ue925',
share: '\ue926',
shuf: '\ue927',
sort: '\ue928',
star: '\ue929',
starred: '\ue92a',
tag: '\ue92b',
tg: '\ue92c',
theme: '\ue92d',
time: '\ue92e',
tod: '\ue92f',
trans: '\ue930',
tried: '\ue931',
try: '\ue932',
uncheck: '\ue933',
undo: '\ue934',
week: '\ue935',
x: '\ue936',
},
currentComponent: 'EnRecipes',
sortType: 'Oldest first',
language: [
{
locale: 'da',
title: 'Dansk',
},
{
locale: 'de',
title: 'Deutsch',
},
{
locale: 'en-GB',
title: 'English (UK)',
},
{
locale: 'es',
title: 'Español',
},
{
locale: 'fr',
title: 'Français',
},
{
locale: 'fr-BE',
title: 'Français (BE)',
},
{
locale: 'fr-CA',
title: 'Français (CA)',
},
{
locale: 'fr-CH',
title: 'Français (CH)',
},
{
locale: 'hi',
title: 'हिंदी',
},
{
locale: 'ml',
title: 'മലയാളം',
},
{
locale: 'id',
title: 'Indonesia',
},
{
locale: 'it',
title: 'Italiano',
},
{
locale: 'nb-NO',
title: 'Norsk bokmål',
},
{
locale: 'nl',
title: 'Nederlands',
},
// {
// locale: 'pt',
// title: 'Português',
// },
{
locale: 'pt-BR',
title: 'Português (BR)',
},
{
locale: 'ru',
title: 'Русский',
},
{
locale: 'ta',
title: 'தமிழ்',
},
{
locale: 'te',
title: 'తెలుగు',
},
],
shakeEnabled: true,
importSummary: {
found: 0,
imported: 0,
updated: 0,
},
layout: 'detailed',
selectedCuisine: null,
selectedCategory: null,
selectedTag: null,
appTheme: 'Light',
mondayFirst: false,
},
mutations: {
clearImportSummary(state) {
for (const key in state.importSummary) state.importSummary[key] = 0
},
setFirstDay(state, bool) {
state.mondayFirst = bool
ApplicationSettings.setBoolean('mondayFirst', bool)
},
setTheme(state, theme) {
state.appTheme = theme
ApplicationSettings.setString('appTheme', theme)
},
clearFilter(state) {
state.selectedCuisine = state.selectedCategory = state.selectedTag = null
},
setLayout(state, type) {
state.layout = type
},
setSortType(state, sortType) {
state.sortType = sortType
},
initRecipes(state) {
EnRecipesDB.query({ select: [] }).forEach((r) => {
if (r.timeRequired) {
r.prepTime = '00:00'
r.cookTime = r.timeRequired
delete r.timeRequired
}
if (!r.hasOwnProperty('cuisine')) r.cuisine = 'Undefined'
if (!r.hasOwnProperty('tags')) r.tags = []
if (!r.hasOwnProperty('difficulty')) r.difficulty = 'Easy'
if (!r.hasOwnProperty('rating')) r.rating = 0
if (!r.hasOwnProperty('created')) r.created = r.lastModified
// if (!r.hasOwnProperty("inBag"))
// r.inBag = false
state.recipes.push(r)
})
state.shakeEnabled = ApplicationSettings.getBoolean('shakeEnabled', true)
state.sortType = ApplicationSettings.getString('sortType', 'Oldest first')
},
importRecipes(state, recipes) {
let localRecipesIDs, partition
let imported = 0
let updated = 0
function getUpdatedData(data) {
return data.map((recipe) => {
let r = Object.assign({}, recipe)
if (r.timeRequired) {
r.prepTime = '00:00'
r.cookTime = r.timeRequired
delete r.timeRequired
}
if (!r.hasOwnProperty('cuisine')) r.cuisine = 'Undefined'
if (!r.hasOwnProperty('tags')) r.tags = []
if (!r.hasOwnProperty('difficulty')) r.difficulty = 'Easy'
if (!r.hasOwnProperty('rating')) r.rating = 0
if (!r.hasOwnProperty('created')) r.created = r.lastModified
// if (!r.hasOwnProperty("inBag"))
// r.inBag = false
return r
})
}
function createDocuments(data) {
data = getUpdatedData(data)
state.recipes = [...state.recipes, ...data]
data.forEach((recipe) => {
imported++
EnRecipesDB.createDocument(recipe, recipe.id)
})
}
function updateDocuments(data) {
data = getUpdatedData(data)
data.forEach((recipe) => {
let recipeIndex = state.recipes
.map((e, i) => {
let d1 = new Date(e.lastModified).getTime()
let d2 = new Date(recipe.lastModified).getTime()
return e.id === recipe.id && d1 < d2 ? i : -1
})
.filter((e) => e >= 0)[0]
if (recipeIndex >= 0) {
updated++
Object.assign(state.recipes[recipeIndex], recipe)
EnRecipesDB.updateDocument(recipe.id, recipe)
}
})
}
if (state.recipes.length) {
localRecipesIDs = state.recipes.map((e) => e.id)
partition = recipes.reduce(
(result, recipe, i) => {
localRecipesIDs.indexOf(recipe.id) < 0
? result[0].push(recipe) // create candidates
: result[1].push(recipe) // update candidates
return result
},
[[], []]
)
if (partition[0].length) createDocuments(partition[0])
if (partition[1].length) updateDocuments(partition[1])
} else {
createDocuments(recipes)
}
state.importSummary.found = recipes.length
state.importSummary.imported = imported
state.importSummary.updated = updated
},
addRecipe(state, { id, recipe }) {
state.recipes.push(recipe)
EnRecipesDB.createDocument(recipe, id)
},
overwriteRecipe(state, { id, recipe }) {
let index = state.recipes.indexOf(
state.recipes.filter((e) => e.id === id)[0]
)
Object.assign(state.recipes[index], recipe)
EnRecipesDB.updateDocument(id, recipe)
},
deleteRecipe(state, { index, id }) {
getFileAccess().deleteFile(state.recipes[index].imageSrc)
state.recipes.splice(index, 1)
EnRecipesDB.deleteDocument(id)
state.recipes.forEach((e, i) => {
if (e.combinations.includes(id)) {
state.recipes[i].combinations.splice(e.combinations.indexOf(id), 1)
EnRecipesDB.updateDocument(state.recipes[i].id, state.recipes[i])
}
})
},
deleteRecipes(state, ids) {
ids.forEach((id) => {
let index = state.recipes.findIndex((e) => e.id === id)
getFileAccess().deleteFile(state.recipes[index].imageSrc)
state.recipes.splice(index, 1)
EnRecipesDB.deleteDocument(id)
state.recipes.forEach((e, i) => {
if (e.combinations.includes(id)) {
state.recipes[i].combinations.splice(e.combinations.indexOf(id), 1)
EnRecipesDB.updateDocument(state.recipes[i].id, state.recipes[i])
}
})
})
},
initListItems(state) {
function initialize(listName) {
let userItems
let db = listItems[listName].db
let key = listItems[listName].key
let stateName = listItems[listName].stateName
let defaultItems = listItems[listName].defaultItems
if (!state[stateName].length) {
let isStored = db.query({ select: [] }).length
if (isStored) {
userItems = db.getDocument(key)[key]
if (userItems.some((e) => defaultItems.includes(e)))
state[stateName] = userItems
else state[stateName] = [...defaultItems, ...userItems]
} else {
if (listItems[listName].sort) {
state[stateName].sort()
}
state[stateName] = defaultItems
db.createDocument(
{
[key]: [],
},
key
)
}
}
}
;['cuisines', 'categories', 'yieldUnits', 'units'].forEach((item) => {
initialize(item)
})
},
importListItems(state, { data, listName }) {
let db = listItems[listName].db
let key = listItems[listName].key
let stateName = listItems[listName].stateName
let items = new Set([...state[stateName], ...data])
state[stateName] = [...items]
if (listItems[listName].sort) state[stateName].sort()
db.updateDocument(key, { [key]: state[stateName] })
},
addListItem(state, { item, listName }) {
let db = listItems[listName].db
let key = listItems[listName].key
let stateName = listItems[listName].stateName
let lowercase = state[stateName].map((e) => e.toLowerCase())
if (lowercase.indexOf(item.toLowerCase()) == -1) {
state[stateName].push(item)
db.updateDocument(key, { [key]: state[stateName] })
if (listItems[listName].sort)
state[stateName].sort((a, b) =>
a.toLowerCase().localeCompare(b.toLowerCase())
)
}
},
removeListItem(state, { item, listName }) {
let db = listItems[listName].db
let key = listItems[listName].key
let stateName = listItems[listName].stateName
let index = state[stateName].indexOf(item)
state[stateName].splice(index, 1)
db.updateDocument(key, { [key]: state[stateName] })
if (listItems[listName].sort) state[stateName].sort()
},
resetListItems(state, listName) {
let stateName = listItems[listName].stateName
let defaultItems = listItems[listName].defaultItems
state[listName] = [...defaultItems]
if (listItems[listName].sort) {
state[stateName].sort()
}
},
initMealPlans(state) {
let isMealPlansDBStored = mealPlansDB.query({ select: [] }).length
if (isMealPlansDBStored) {
let plans = mealPlansDB.getDocument('mealPlans').mealPlans
if (plans.length && plans[0].hasOwnProperty('eventColor')) {
plans.forEach((p) => {
let d = new Date(p.startDate)
p.date = new Date(
d.getFullYear(),
d.getMonth(),
d.getDate(),
0
).getTime()
switch (new Date(p.date).getHours()) {
case 0:
p.type = 'breakfast'
break
case 5:
p.type = 'lunch'
break
case 10:
p.type = 'dinner'
break
case 15:
p.type = 'snacks'
break
}
delete p.startDate
delete p.endDate
delete p.eventColor
state.mealPlans.push(p)
})
mealPlansDB.updateDocument('mealPlans', {
mealPlans: state.mealPlans,
})
} else state.mealPlans = [...plans]
} else {
mealPlansDB.createDocument(
{
mealPlans: [],
},
'mealPlans'
)
}
},
importMealPlans(state, mealPlans) {
let newMealPlans = mealPlans.filter((e) => {
if (e.hasOwnProperty('eventColor')) {
return !state.mealPlans.some((f) => {
let d = new Date(e.startDate)
let date = new Date(
d.getFullYear(),
d.getMonth(),
d.getDate(),
0
).getTime()
let type
switch (d.getHours()) {
case 0:
type = 'breakfast'
break
case 5:
type = 'lunch'
break
case 10:
type = 'dinner'
break
case 15:
type = 'snacks'
break
}
return f.title === e.title && f.date === date && f.type === type
})
} else {
return !state.mealPlans.some(
(f) => f.title === e.title && f.date === e.date && f.type === e.type
)
}
})
let updatedMealPlans = []
if (newMealPlans[0].hasOwnProperty('eventColor')) {
newMealPlans.forEach((p) => {
let d = new Date(p.startDate)
p.date = new Date(
d.getFullYear(),
d.getMonth(),
d.getDate(),
0
).getTime()
switch (d.getHours()) {
case 0:
p.type = 'breakfast'
break
case 5:
p.type = 'lunch'
break
case 10:
p.type = 'dinner'
break
case 15:
p.type = 'snacks'
break
}
delete p.startDate
delete p.endDate
delete p.eventColor
updatedMealPlans.push(p)
})
}
state.mealPlans = [...state.mealPlans, ...updatedMealPlans]
mealPlansDB.updateDocument('mealPlans', { mealPlans: state.mealPlans })
},
addMealPlan(state, { title, date, type, index }) {
let mealPlan = {
title,
date,
type,
}
if (index != null) state.mealPlans.splice(index, 0, mealPlan)
else state.mealPlans.push(mealPlan)
mealPlansDB.updateDocument('mealPlans', {
mealPlans: [...state.mealPlans],
})
},
deleteMealPlan(state, { title, date, type, index }) {
state.mealPlans.splice(index, 1)
state.mealPlans = [...state.mealPlans]
mealPlansDB.updateDocument('mealPlans', {
mealPlans: [...state.mealPlans],
})
},
toggleState(state, { id, recipe, key, setDate }) {
let index = state.recipes.indexOf(
state.recipes.filter((e) => e.id === id)[0]
)
state.recipes[index][key] = !state.recipes[index][key]
if (setDate) state.recipes[index].lastTried = new Date()
EnRecipesDB.updateDocument(id, recipe)
},
setRecipeAsTried(state, { id, recipe }) {
let index = state.recipes.indexOf(
state.recipes.filter((e) => e.id === id)[0]
)
state.recipes[index].tried = true
state.recipes[index].lastTried = new Date()
EnRecipesDB.updateDocument(id, recipe)
},
setLastTriedDate(state, index) {
state.recipes[index].lastTried = new Date()
EnRecipesDB.updateDocument(state.recipes[index].id, state.recipes[index])
},
setComponent(state, comp) {
state.currentComponent = comp
},
unSyncCombinations(state, { id, combinations }) {
state.recipes.forEach((e, i) => {
if (combinations.includes(e.id)) {
state.recipes[i].combinations.splice(e.combinations.indexOf(id), 1)
EnRecipesDB.updateDocument(state.recipes[i].id, state.recipes[i])
}
})
},
setShake(state, shake) {
state.shakeEnabled = shake
ApplicationSettings.setBoolean('shakeEnabled', shake)
},
setRating(state, { id, recipe, rating }) {
let index = state.recipes.indexOf(
state.recipes.filter((e) => e.id === id)[0]
)
state.recipes[index].rating = rating
EnRecipesDB.updateDocument(id, recipe)
},
toggleCart(state, { id, recipe }) {
let index = state.recipes.indexOf(
state.recipes.filter((e) => e.id === id)[0]
)
state.recipes[index].inBag = !state.recipes[index].inBag
EnRecipesDB.updateDocument(id, recipe)
},
unlinkBrokenImages(state) {
state.recipes.forEach((r, i) => {
if (r.imageSrc && !File.exists(r.imageSrc)) {
r.imageSrc = null
Object.assign(state.recipes[i], r)
EnRecipesDB.updateDocument(r.id, r)
}
})
},
setCuisine(state, value) {
state.selectedCuisine = value
},
setCategory(state, value) {
state.selectedCategory = value
},
setTag(state, value) {
state.selectedTag = value
},
},
actions: {
clearImportSummary({ commit }) {
commit('clearImportSummary')
},
setFirstDay({ commit }, bool) {
commit('setFirstDay', bool)
},
setTheme({ commit }, theme) {
commit('setTheme', theme)
},
clearFilter({ commit }) {
commit('clearFilter')
},
setLayout({ commit }, type) {
commit('setLayout', type)
},
setSortType({ commit }, sortType) {
commit('setSortType', sortType)
},
initRecipes({ commit }) {
commit('initRecipes')
},
importRecipesAction({ commit }, recipes) {
commit('importRecipes', recipes)
},
addRecipeAction({ commit }, recipe) {
commit('addRecipe', recipe)
},
overwriteRecipeAction({ commit }, updatedRecipe) {
commit('overwriteRecipe', updatedRecipe)
},
deleteRecipeAction({ commit }, recipe) {
commit('deleteRecipe', recipe)
},
deleteRecipesAction({ commit }, ids) {
commit('deleteRecipes', ids)
},
initListItems({ commit }) {
commit('initListItems')
},
importListItemsAction({ commit }, data) {
commit('importListItems', data)
},
addListItemAction({ commit }, item) {
commit('addListItem', item)
},
removeListItemAction({ commit }, item) {
commit('removeListItem', item)
},
resetListItemsAction({ commit }, listName) {
commit('resetListItems', listName)
},
initMealPlans({ commit }) {
commit('initMealPlans')
},
importMealPlansAction({ commit }, mealPlans) {
commit('importMealPlans', mealPlans)
},
addMealPlanAction({ commit }, mealPlan) {
commit('addMealPlan', mealPlan)
},
deleteMealPlanAction({ commit }, mealPlan) {
commit('deleteMealPlan', mealPlan)
},
toggleStateAction({ commit }, toggledRecipe) {
commit('toggleState', toggledRecipe)
},
setRecipeAsTriedAction({ commit }, recipe) {
commit('setRecipeAsTried', recipe)
},
setLastTriedDateAction({ commit }, index) {
commit('setLastTriedDate', index)
},
setComponent({ commit }, comp) {
commit('setComponent', comp)
},
unSyncCombinationsAction({ commit }, combinations) {
commit('unSyncCombinations', combinations)
},
setShake({ commit }, shake) {
commit('setShake', shake)
},
setRatingAction({ commit }, rating) {
commit('setRating', rating)
},
toggleCartAction({ commit }, recipe) {
commit('toggleCart', recipe)
},
unlinkBrokenImages({ commit }) {
commit('unlinkBrokenImages')
},
setCuisine({ commit }, value) {
commit('setCuisine', value)
},
setCategory({ commit }, value) {
commit('setCategory', value)
},
setTag({ commit }, value) {
commit('setTag', value)
},
},
})