diff --git a/app/app.scss b/app/app.scss index 281d714a..8dc44d12 100644 --- a/app/app.scss +++ b/app/app.scss @@ -202,6 +202,7 @@ MDActivityIndicator { MDProgress { progress-color: $orange; } + // ----------------------------- // ActionBar ActionBar { @@ -480,6 +481,7 @@ RadListView { .recipeLink { padding: 16; margin: 0; + line-height: 6; } } .recipeText { @@ -522,6 +524,10 @@ MDButton.closeBtn { min-width: 0; vertical-alignment: top; } +TextField.combinationToken { + border-width: 0; + background: white; +} // ----------------------------- // DIALOGS .dialogContainer { @@ -547,6 +553,7 @@ MDButton.closeBtn { .actionItem { letter-spacing: 0; text-transform: none; + line-height: 6; padding: 16 24; margin: 0; } diff --git a/app/components/EditRecipe.vue b/app/components/EditRecipe.vue index 537a9fb2..a2984641 100644 --- a/app/components/EditRecipe.vue +++ b/app/components/EditRecipe.vue @@ -227,7 +227,7 @@ - + + + + @@ -273,6 +303,7 @@ import { path, Screen, Utils, + ObservableArray, } from "@nativescript/core" import * as Permissions from "@nativescript-community/perms" import * as Toast from "nativescript-toast" @@ -281,13 +312,13 @@ import { ImageCropper } from "nativescript-imagecropper" import { mapState, mapActions } from "vuex" import ActionDialog from "./modal/ActionDialog.vue" +import ActionDialogWithSearch from "./modal/ActionDialogWithSearch.vue" import ConfirmDialog from "./modal/ConfirmDialog.vue" import PromptDialog from "./modal/PromptDialog.vue" import ListPicker from "./modal/ListPicker.vue" export default { props: [ - "recipeIndex", "recipeID", "selectedCategory", "openAppSettingsPage", @@ -311,6 +342,7 @@ export default { instructions: [], notes: [], references: [], + combinations: [], isFavorite: false, tried: false, lastTried: null, @@ -323,6 +355,7 @@ export default { showFab: false, imageLoading: false, cacheImagePath: null, + unSyncCombinations: [], } }, computed: { @@ -360,6 +393,7 @@ export default { "overwriteRecipeAction", "addCategoryAction", "addYieldUnitAction", + "unSyncCombinationsAction", ]), onPageLoad() { this.showFab = true @@ -367,7 +401,7 @@ export default { // HELPERS focusField(args, type) { - this.setInputTypeText(args, type) + if (type) this.setInputTypeText(args, type) if (!args.object.text) { args.object.focus() setTimeout((e) => Utils.ad.showSoftInput(args.object.android), 1) @@ -719,15 +753,46 @@ export default { } else this.recipeContent.instructions.splice(index, 1) }, + getCombinationTitle(id) { + return this.recipes.filter((e) => e.id === id)[0].title + }, + showCombinations() { + let existingCombinations = [ + ...this.recipeContent.combinations, + this.recipeContent.id, + ] + console.log(existingCombinations) + let filteredRecipes = this.recipes.filter( + (e) => !existingCombinations.includes(e.id) + ) + this.$showModal(ActionDialogWithSearch, { + props: { + title: "Select a recipe", + recipes: filteredRecipes, + }, + }).then((res) => { + if (res) { + this.recipeContent.combinations.push(res) + } + }) + }, + removeCombination(id) { + let index = this.recipeContent.combinations.indexOf(id) + this.fieldDeletionConfirm("combination").then((res) => { + if (res) { + this.recipeContent.combinations.splice(index, 1) + this.unSyncCombinations.push(id) + } + }) + }, + addNote() { this.recipeContent.notes.push("") }, removeNote(index) { if (this.recipeContent.notes[index].length) { this.fieldDeletionConfirm("note").then((res) => { - if (res) { - this.recipeContent.notes.splice(index, 1) - } + if (res) this.recipeContent.notes.splice(index, 1) }) } else this.recipeContent.notes.splice(index, 1) }, @@ -789,12 +854,15 @@ export default { } else if (this.tempRecipeContent.imageSrc) { getFileAccess().deleteFile(this.tempRecipeContent.imageSrc) } + this.unSyncCombinationsAction({ + id: this.recipeID, + combinations: this.unSyncCombinations, + }) this.saveRecipe() }, saveRecipe() { if (this.recipeID) { this.overwriteRecipeAction({ - index: this.recipeIndex, id: this.recipeID, recipe: this.recipeContent, }) diff --git a/app/components/EnRecipes.vue b/app/components/EnRecipes.vue index 9cf37b31..88f784c1 100644 --- a/app/components/EnRecipes.vue +++ b/app/components/EnRecipes.vue @@ -384,7 +384,6 @@ export default { // }, props: { filterTrylater: this.filterTrylater, - recipeIndex: index, recipeID: item.id, hijackGlobalBackEvent: this.hijackGlobalBackEvent, releaseGlobalBackEvent: this.releaseGlobalBackEvent, diff --git a/app/components/ViewRecipe.vue b/app/components/ViewRecipe.vue index 6225078b..096d8a17 100644 --- a/app/components/ViewRecipe.vue +++ b/app/components/ViewRecipe.vue @@ -13,8 +13,8 @@ /> @@ -102,8 +102,8 @@ + + + + + + + + + + + + + import { + ApplicationSettings, Color, Device, File, @@ -457,6 +521,7 @@ import { Application } from "@nativescript/core" import { mapActions, mapState } from "vuex" import EditRecipe from "./EditRecipe.vue" +import ViewRecipe from "./ViewRecipe.vue" import ShareChooser from "./modal/ShareChooser.vue" let feedback = new Feedback() @@ -464,8 +529,8 @@ let feedback = new Feedback() export default { props: [ "filterTrylater", - "recipeIndex", "recipeID", + "recipeIndex", "hijackGlobalBackEvent", "releaseGlobalBackEvent", ], @@ -476,6 +541,7 @@ export default { recipe: null, showFab: false, selectedTabIndex: 0, + currentRecipeID: this.recipeID, } }, computed: { @@ -491,7 +557,11 @@ export default { }, }, methods: { - ...mapActions(["toggleStateAction", "setCurrentComponentAction"]), + ...mapActions([ + "toggleStateAction", + "setCurrentComponentAction", + "overwriteRecipeAction", + ]), onPageLoad() { this.releaseGlobalBackEvent() this.busy = false @@ -501,6 +571,7 @@ export default { this.yieldMultiplier = this.recipe.yield.quantity this.showFab = true this.keepScreenOn(true) + this.syncCombinations() }, onPageUnload() { feedback.hide() @@ -569,24 +640,45 @@ export default { let pattern = new RegExp("^https?|www", "ig") return pattern.test(string) }, + getCombinationTitle(id) { + return this.recipes.filter((e) => e.id === id)[0].title + }, + syncCombinations() { + let combinationForOtherRecipes = this.recipes + .filter( + (e) => + e.combinations.indexOf(this.currentRecipeID) >= 0 || + this.recipe.combinations.includes(e.id) + ) + .map((e) => e.id) + this.recipe.combinations = combinationForOtherRecipes + this.overwriteRecipeAction({ + id: this.currentRecipeID, + recipe: this.recipe, + }) + }, // NAVIGATION HANDLERS editRecipe() { this.showFab = false this.busy = true this.$navigateTo(EditRecipe, { - // transition: { - // name: "slide", - // duration: 250, - // curve: "easeOut", - // }, props: { - recipeIndex: this.recipeIndex, - recipeID: this.recipeID, + recipeID: this.currentRecipeID, }, - // backstackVisible: false, }) }, + viewCombination(combination) { + this.recipe = this.recipes.filter((e) => e.id === combination)[0] + this.currentRecipeID = combination + this.syncCombinations() + this.selectedTabIndex = 0 + setTimeout( + (e) => + this.recipe.tried && this.recipe.lastTried && this.showLastTried(), + 500 + ) + }, // SHARE ACTION shareHandler() { @@ -598,20 +690,6 @@ export default { }).then((result) => { switch (result) { case "photo": - // let cacheFilePath = path.join( - // knownFolders.temp().path, - // `${this.recipe.title}.jpg` - // ) - // if (!File.exists(cacheFilePath)) { - // File.fromPath(cacheFilePath).writeSync( - // File.fromPath(this.recipe.imageSrc).readSync() - // ) - // } - // let shareFile = new ShareFile() - // shareFile.open({ - // path: cacheFilePath, - // title: "Share recipe photo using", - // }) ImageSource.fromFile(this.recipe.imageSrc).then((res) => { SocialShare.shareImage(res, "Share recipe photo using") }) @@ -653,6 +731,13 @@ export default { }) shareContent += instructions } + if (this.recipe.combinations.length) { + let combinations = `\nCombinations:\n\n` + this.recipe.combinations.forEach((e, i) => { + combinations += `${i + 1}. ${this.getCombinationTitle(e)}\n\n` + }) + shareContent += combinations + } if (this.recipe.notes.length) { let notes = `\nNotes:\n\n` this.recipe.notes.forEach((e, i) => { @@ -677,8 +762,7 @@ export default { // DATA HANDLERS toggle(key, setDate) { this.toggleStateAction({ - index: this.recipeIndex, - id: this.recipeID, + id: this.currentRecipeID, recipe: this.recipe, key, setDate, @@ -716,7 +800,7 @@ export default { }, }, created() { - this.recipe = this.recipes.filter((e) => e.id === this.recipeID)[0] + this.recipe = this.recipes.filter((e) => e.id === this.currentRecipeID)[0] }, mounted() { this.showFab = true diff --git a/app/components/modal/ActionDialogWithSearch.vue b/app/components/modal/ActionDialogWithSearch.vue new file mode 100644 index 00000000..39a6042e --- /dev/null +++ b/app/components/modal/ActionDialogWithSearch.vue @@ -0,0 +1,94 @@ + + + diff --git a/app/main.js b/app/main.js index 846ad6f1..11018eae 100644 --- a/app/main.js +++ b/app/main.js @@ -20,9 +20,6 @@ Vue.use(FloatingActionButtonPlugin) import ProgressPlugin from "@nativescript-community/ui-material-progress/vue" Vue.use(ProgressPlugin) -// import SpeedDialPlugin from "@nativescript-community/ui-material-speeddial/vue" -// Vue.use(SpeedDialPlugin) - Vue.registerElement( "RadSideDrawer", () => require("nativescript-ui-sidedrawer").RadSideDrawer diff --git a/app/store.js b/app/store.js index b46497e0..9a1ffe9c 100644 --- a/app/store.js +++ b/app/store.js @@ -182,6 +182,7 @@ export default new Vuex.Store({ source: "\ueaa0", export: "\ued07", import: "\ued0c", + outline: "\ueb07", }, currentComponent: "EnRecipes", }, @@ -306,7 +307,10 @@ export default new Vuex.Store({ }) state.yieldUnits = [...defaultYieldUnits, ...state.userYieldUnits] }, - overwriteRecipe(state, { index, id, recipe }) { + 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) }, @@ -314,8 +318,17 @@ export default new Vuex.Store({ 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]) + } + }) }, - toggleState(state, { index, id, recipe, key, setDate }) { + 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) @@ -346,6 +359,14 @@ export default new Vuex.Store({ } }) }, + 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]) + } + }) + }, }, actions: { initializeRecipes({ commit }) { @@ -393,5 +414,8 @@ export default new Vuex.Store({ renameCategoryAction({ commit }, category) { commit("renameCategory", category) }, + unSyncCombinationsAction({ commit }, combinations) { + commit("unSyncCombinations", combinations) + }, }, })