bug fixes, added localize, minor ui improvements

This commit is contained in:
vishnuraghavb 2020-12-07 20:15:00 +05:30
parent 77e022eb58
commit 62c25fcf22
3366 changed files with 18283 additions and 16865 deletions

View file

@ -25,7 +25,7 @@ EnRecipes is an easy to use, privacy-friendly digital cookbook that lets you cre
- Add photo, notes and combinations to your recipes - Add photo, notes and combinations to your recipes
- Organise your recipes by category - Organise your recipes by category
- Quickly search for your recipes - Quickly search for your recipes
- Mark recipes as favorites and add them to your Try Later list - Mark recipes as favourites and add them to your Try Later list
- Scale your recipe ingredients to serve more or less people - Scale your recipe ingredients to serve more or less people
- Get notified of the last time you tried a recipe - Get notified of the last time you tried a recipe
- Share your recipe to anyone by any means as a nicely formatted message. You can share the recipe photo too. - Share your recipe to anyone by any means as a nicely formatted message. You can share the recipe photo too.

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">EnRecipes</string>
<string name="title_activity_kimera">EnRecipes</string>
</resources>

View file

@ -1,7 +1,6 @@
// NativeScript core theme // NativeScript core theme
// @see https://docs.nativescript.org/ui/theme // @see https://docs.nativescript.org/ui/theme
@import "~@nativescript/theme/core"; @import "~@nativescript/theme/core"; // Override variables here
// Override variables here
$gray1: #f1f3f5; $gray1: #f1f3f5;
$gray2: #e9ecef; $gray2: #e9ecef;
$gray3: #dee2e6; $gray3: #dee2e6;
@ -17,20 +16,17 @@ $red: #c92a2a;
$breakfast: #ffb180; $breakfast: #ffb180;
$lunch: #ceff80; $lunch: #ceff80;
$dinner: #80ceff; $dinner: #80ceff;
$snacks: #b180ff; $snacks: #b180ff; // Global SCSS styling
// Global SCSS styling
// @see https://docs.nativescript.org/ui/styling // @see https://docs.nativescript.org/ui/styling
.ns-modal,
Page, Page {
.ns-modal { font-family: 'Orkney-Regular';
font-family: "Orkney-Regular";
} }
.orkm { .orkm {
font-family: "Orkney-Medium"; font-family: 'Orkney-Medium';
} }
.bx { .bx {
font-family: "boxicons"; font-family: 'boxicons';
font-size: 24; font-size: 24;
vertical-alignment: center; vertical-alignment: center;
&.small { &.small {
@ -39,38 +35,37 @@ Page,
} }
} }
.ns-light { .ns-light {
Page,
ActionBar, ActionBar,
ListPicker,
Page,
SearchBar, SearchBar,
Tabs,
TabStripItem, TabStripItem,
ListPicker { Tabs {
color: $gray9; color: $gray9;
background: $gray1; background: $gray1;
} }
MDRipple, MDButton,
MDButton { MDRipple {
ripple-color: rgba($gray6, 0.2); ripple-color: rgba($gray6, 0.2);
} }
.hr { .hr {
border-color: $gray3; border-color: $gray3;
} }
.sd, .fieldLabel,
.fieldLabel { .sd {
background: $gray1; background: $gray1;
} }
.urlCard,
.textCard,
.overviewItem, .overviewItem,
.recipeItem { .recipeItem,
.textCard,
.urlCard {
background: white; background: white;
} }
TextField.combinationToken { TextField.combinationToken {
background: $gray3; background: $gray3;
} }
.sd-item,
.sd-group-header, .sd-group-header,
.sd-item,
.time .bx { .time .bx {
color: $gray8; color: $gray8;
} }
@ -87,8 +82,8 @@ Page,
color: $gray1; color: $gray1;
background: $gray9; background: $gray9;
} }
.instruction, .dayContainer,
.dayContainer { .instruction {
border-color: $gray9; border-color: $gray9;
} }
MDProgress { MDProgress {
@ -101,14 +96,13 @@ Page,
background: $orange; background: $orange;
} }
} }
.ns-dark { .ns-dark {
Page,
ActionBar, ActionBar,
ListPicker,
Page,
SearchBar, SearchBar,
Tabs,
TabStripItem, TabStripItem,
ListPicker { Tabs {
color: $gray1; color: $gray1;
background: $gray9; background: $gray9;
} }
@ -116,27 +110,26 @@ Page,
// tab-background-color: $gray9; // tab-background-color: $gray9;
// selected-tab-text-color: $gray1; // selected-tab-text-color: $gray1;
// } // }
MDRipple, MDButton,
MDButton { MDRipple {
ripple-color: rgba($gray4, 0.1); ripple-color: rgba($gray4, 0.1);
} }
.hr { .hr {
border-color: #111; border-color: #111;
} }
.sd, .fieldLabel,
.fieldLabel { .sd {
background: $gray9; background: $gray9;
} }
.urlCard,
.textCard,
.overviewItem, .overviewItem,
.recipeItem, .recipeItem,
.textCard,
.urlCard,
TextField.combinationToken { TextField.combinationToken {
background: $gray8; background: $gray8;
} }
.sd-item,
.sd-group-header, .sd-group-header,
.sd-item,
.time .bx { .time .bx {
color: $gray3; color: $gray3;
} }
@ -153,8 +146,8 @@ Page,
color: $gray9; color: $gray9;
background: $gray1; background: $gray1;
} }
.instruction, .dayContainer,
.dayContainer { .instruction {
border-color: $gray1; border-color: $gray1;
} }
MDProgress { MDProgress {
@ -176,7 +169,7 @@ TimePickerField {
border-width: 1; border-width: 1;
font-size: 14; font-size: 14;
padding: 14 14 13; padding: 14 14 13;
margin: 8 0 0 0; margin: 8 0 0;
border-radius: 4; border-radius: 4;
border-color: $gray6; border-color: $gray6;
placeholder-color: $gray6; placeholder-color: $gray6;
@ -184,8 +177,11 @@ TimePickerField {
TextView { TextView {
line-height: 12; line-height: 12;
} }
ListPicker {
width: 25%;
}
SearchBar { SearchBar {
font-family: "Orkney-Regular"; font-family: 'Orkney-Regular';
font-size: 16; font-size: 16;
text-field-hint-color: $gray6; text-field-hint-color: $gray6;
} }
@ -203,16 +199,15 @@ TabView {
.progressContainer { .progressContainer {
width: 100%; width: 100%;
} }
.text-btn,
.group-header,
.category, .category,
.group-header,
.text-btn,
MDActivityIndicator { MDActivityIndicator {
color: $orange; color: $orange;
} }
MDProgress { MDProgress {
progress-color: $orange; progress-color: $orange;
} }
// ----------------------------- // -----------------------------
// ActionBar // ActionBar
ActionBar { ActionBar {
@ -267,10 +262,13 @@ ActionBar {
MDRipple { MDRipple {
padding: 0 16; padding: 0 16;
} }
MDButton.bx {
margin: 0;
}
} }
.sd-group-header { .sd-group-header {
width: 100%; width: 100%;
padding: 8; padding: 0 0 8 8;
} }
MDRipple { MDRipple {
border-radius: 4; border-radius: 4;
@ -313,7 +311,7 @@ MDButton {
font-size: 20; font-size: 20;
text-align: center; text-align: center;
padding: 0; padding: 0;
margin: 0; margin: 0 0 8;
horizontal-alignment: center; horizontal-alignment: center;
.bx { .bx {
font-size: 24; font-size: 24;
@ -404,8 +402,8 @@ RadListView {
// VIEW RECIPE // VIEW RECIPE
.viewRecipe { .viewRecipe {
.category, .category,
.time, .ingredient,
.ingredient { .time {
font-size: 16; font-size: 16;
} }
.category { .category {
@ -413,14 +411,15 @@ RadListView {
} }
.time { .time {
margin: 0 8; margin: 0 8;
.bx {
vertical-align: top;
}
} }
.title { .title {
font-size: 22; font-size: 22;
line-height: 6; line-height: 6;
} }
.subTitle {
font-size: 18;
line-height: 4;
}
.overviewContainer { .overviewContainer {
margin-top: 12; margin-top: 12;
.overviewItem { .overviewItem {
@ -507,7 +506,7 @@ MDFloatingActionButton {
font-size: 14; font-size: 14;
horizontal-alignment: left; horizontal-alignment: left;
padding: 12; padding: 12;
margin: 8 0 0 0; margin: 8 0 0;
min-width: 0; min-width: 0;
} }
MDButton.closeBtn { MDButton.closeBtn {
@ -544,7 +543,7 @@ MDButton.closeBtn {
padding: 0 0 0 16; padding: 0 0 0 16;
} }
.recipes { .recipes {
margin: 4 8 4; margin: 4 8;
.recipeTitle { .recipeTitle {
font-size: 14; font-size: 14;
padding: 6 8; padding: 6 8;
@ -555,8 +554,8 @@ MDButton.closeBtn {
vertical-alignment: top; vertical-alignment: top;
} }
} }
MDRipple, MDButton,
MDButton { MDRipple {
ripple-color: rgba($gray6, 0.2); ripple-color: rgba($gray6, 0.2);
} }
} }
@ -612,7 +611,6 @@ MDButton.closeBtn {
margin: 0 16 16; margin: 0 16 16;
} }
} }
// ----------------------------- // -----------------------------
MDActivityIndicator { MDActivityIndicator {
width: 24; width: 24;
@ -647,7 +645,6 @@ MDActivityIndicator {
75% { 75% {
transform: scale(1.1); transform: scale(1.1);
} }
100% { 100% {
transform: scale(1); transform: scale(1);
} }
@ -656,7 +653,6 @@ MDActivityIndicator {
0% { 0% {
transform: scale(1); transform: scale(1);
} }
100% { 100% {
transform: scale(0); transform: scale(0);
opacity: 0; opacity: 0;

View file

@ -2,85 +2,54 @@
<Page @loaded="onPageLoad"> <Page @loaded="onPageLoad">
<ActionBar :androidElevation="viewIsScrolled ? 4 : 0"> <ActionBar :androidElevation="viewIsScrolled ? 4 : 0">
<GridLayout rows="*" columns="auto, *"> <GridLayout rows="*" columns="auto, *">
<MDButton <MDButton variant="text" class="bx" :text="icon.menu" automationText="Back" @tap="showDrawer" col="0" />
variant="text" <Label class="title orkm" :text="'About' | L" col="1" />
class="bx"
:text="icon.menu"
automationText="Back"
@tap="showDrawer"
col="0"
/>
<Label class="title orkm" text="About" col="1" />
</GridLayout> </GridLayout>
</ActionBar> </ActionBar>
<ScrollView @scroll="onScroll"> <ScrollView @scroll="onScroll">
<StackLayout class="main-container"> <StackLayout class="main-container">
<StackLayout <StackLayout horizontalAlignment="center" orientation="horizontal" class="appIconContainer">
horizontalAlignment="center"
orientation="horizontal"
class="appIconContainer"
>
<Image src="res://logo" class="appIcon" stretch="aspectFit" /> <Image src="res://logo" class="appIcon" stretch="aspectFit" />
</StackLayout> </StackLayout>
<StackLayout class="m-8"></StackLayout> <StackLayout class="m-8"></StackLayout>
<GridLayout columns="auto, *" class="option"> <GridLayout columns="auto, *" class="option">
<Label col="0" class="bx" :text="icon.info" /> <Label col="0" class="bx" :text="icon.info" />
<StackLayout col="1"> <StackLayout col="1">
<Label text="Version" /> <Label :text="'Version' | L" />
<Label :text="getVersion" class="info" textWrap="true" /> <Label :text="getVersion" class="info" textWrap="true" />
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
<GridLayout columns="auto, *" class="option"> <GridLayout columns="auto, *" class="option">
<MDRipple <MDRipple colSpan="2" @tap="openURL('https://github.com/vishnuraghavb/enrecipes')" />
colSpan="2"
@tap="openURL('https://github.com/vishnuraghavb/enrecipes')"
/>
<Label col="0" class="bx" :text="icon.link" /> <Label col="0" class="bx" :text="icon.link" />
<Label <Label verticalAlignment="center" col="1" :text="'View project on GitHub' | L" textWrap="true" />
verticalAlignment="center"
col="1"
text="View project on GitHub"
/>
</GridLayout> </GridLayout>
<GridLayout columns="auto, *" class="option"> <GridLayout columns="auto, *" class="option">
<MDRipple colSpan="2" @tap="openURL('https://t.me/enrecipes')" /> <MDRipple colSpan="2" @tap="openURL('https://t.me/enrecipes')" />
<Label col="0" class="bx" :text="icon.telegram" /> <Label col="0" class="bx" :text="icon.telegram" />
<StackLayout col="1"> <StackLayout col="1">
<Label text="Join the Telegram group" /> <Label :text="'Join the Telegram group' | L" textWrap="true" />
<Label <Label :text="'for reporting issues, suggestions and feedback' | L" class="info" textWrap="true" />
text="for reporting issues, suggestions and feedback"
class="info"
textWrap="true"
/>
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
<StackLayout class="hr m-10"></StackLayout> <StackLayout class="hr m-10"></StackLayout>
<Label text="Author" class="group-header orkm" /> <Label :text="'Author' | L" class="group-header orkm" />
<GridLayout columns="auto, *" class="option"> <GridLayout columns="auto, *" class="option">
<MDRipple <MDRipple colSpan="2" @tap="openURL('https://www.vishnuraghav.com')" />
colSpan="2"
@tap="openURL('https://www.vishnuraghav.com')"
/>
<Label col="0" class="bx" :text="icon.user" /> <Label col="0" class="bx" :text="icon.user" />
<Label verticalAlignment="center" col="1" text="Vishnu Raghav B" /> <Label verticalAlignment="center" col="1" :text="'Vishnu Raghav B' | L" textWrap="true" />
</GridLayout> </GridLayout>
<GridLayout columns="auto, *" class="option"> <GridLayout columns="auto, *" class="option">
<MDRipple <MDRipple colSpan="2" @tap="openURL('https://github.com/vishnuraghavb')" />
colSpan="2"
@tap="openURL('https://github.com/vishnuraghavb')"
/>
<Label col="0" class="bx" :text="icon.link" /> <Label col="0" class="bx" :text="icon.link" />
<Label verticalAlignment="center" col="1" text="Follow on GitHub" /> <Label verticalAlignment="center" col="1" :text="'Follow on GitHub' | L" textWrap="true" />
</GridLayout> </GridLayout>
<GridLayout columns="auto, *" class="option"> <GridLayout columns="auto, *" class="option">
<MDRipple <MDRipple colSpan="2" @tap="openURL('https://mastodon.social/@vishnuraghavb')" />
colSpan="2"
@tap="openURL('https://mastodon.social/@vishnuraghavb')"
/>
<Label col="0" class="bx" :text="icon.link" /> <Label col="0" class="bx" :text="icon.link" />
<Label verticalAlignment="center" col="1" text="Follow on Mastodon" /> <Label verticalAlignment="center" col="1" :text="'Follow on Mastodon' | L" textWrap="true" />
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
@ -88,8 +57,15 @@
</template> </template>
<script> <script>
import { Application, Utils } from "@nativescript/core" import {
import { mapActions, mapState } from "vuex" Application,
Utils,
Observable
} from "@nativescript/core"
import {
mapActions,
mapState
} from "vuex"
import * as utils from "~/shared/utils" import * as utils from "~/shared/utils"
@ -110,7 +86,9 @@ export default {
}, },
methods: { methods: {
...mapActions(["setCurrentComponentAction"]), ...mapActions(["setCurrentComponentAction"]),
onPageLoad() { onPageLoad(args) {
const page = args.object;
page.bindingContext = new Observable();
this.setCurrentComponentAction("About") this.setCurrentComponentAction("About")
}, },
// HELPERS // HELPERS

View file

@ -1,138 +1,57 @@
<template> <template>
<Page <Page @loaded="onPageLoad" actionBarHidden="true" :androidStatusBarBackground="appTheme == 'Light' ? '#f1f3f5' : '#212529'">
@loaded="onPageLoad"
actionBarHidden="true" <RadSideDrawer allowEdgeSwipe="true" showOverNavigation="true" ref="drawer" id="sideDrawer" drawerContentSize="270" gesturesEnabled="true" drawerTransition="SlideInOnTopTransition">
:androidStatusBarBackground="appTheme == 'Light' ? '#f1f3f5' : '#212529'"
>
<RadSideDrawer
allowEdgeSwipe="true"
showOverNavigation="true"
ref="drawer"
id="sideDrawer"
drawerContentSize="270"
gesturesEnabled="true"
drawerTransition="SlideInOnTopTransition"
>
<GridLayout rows="*, auto" columns="*" ~drawerContent class="sd"> <GridLayout rows="*, auto" columns="*" ~drawerContent class="sd">
<StackLayout row="0"> <StackLayout row="0">
<GridLayout <GridLayout rows="48" columns="auto, 24, *" v-for="(item, index) in topmenu" :key="index" class="sd-item orkm" :class="{
rows="48"
columns="auto, 24, *"
v-for="(item, index) in topmenu"
:key="index"
class="sd-item orkm"
:class="{
'selected-sd-item': currentComponent === item.component, 'selected-sd-item': currentComponent === item.component,
}" }">
> <MDRipple row="0" colSpan="3" @tap="navigateTo(item.component, item.component, false, false)" />
<MDRipple
row="0"
colSpan="3"
@tap="navigateTo(item.component, false, false)"
/>
<Label col="0" row="0" class="bx" :text="icon[item.icon]" /> <Label col="0" row="0" class="bx" :text="icon[item.icon]" />
<Label col="2" row="0" :text="item.title" /> <Label col="2" row="0" :text="`${item.title}` | L" />
</GridLayout> </GridLayout>
<StackLayout class="hr m-8"></StackLayout> <StackLayout class="hr" margin="8"></StackLayout>
<GridLayout <GridLayout rows="48" columns="auto, 24, *" class="sd-item orkm" :class="{
rows="48"
columns="auto, 24, *"
class="sd-item orkm"
:class="{
'selected-sd-item': currentComponent === 'MealPlanner', 'selected-sd-item': currentComponent === 'MealPlanner',
}" }">
> <MDRipple row="0" colSpan="3" @tap="navigateTo(MealPlanner, 'MealPlanner', true, false)" />
<MDRipple
row="0"
colSpan="3"
@tap="navigateTo(MealPlanner, true, false)"
/>
<Label col="0" row="0" class="bx" :text="icon.calendar" /> <Label col="0" row="0" class="bx" :text="icon.calendar" />
<Label col="2" row="0" text="Meal Planner" /> <Label col="2" row="0" :text="'Meal Planner' | L" />
</GridLayout> </GridLayout>
<StackLayout class="hr m-8"></StackLayout> <StackLayout class="hr" margin="8"></StackLayout>
<GridLayout <GridLayout class="sd-group-header orkm" rows="auto" columns="*, auto" v-if="categoriesWithRecipes.length">
class="sd-group-header orkm" <Label verticalAlignment="center" col="0" :text="'Categories' | L" />
rows="auto" <MDButton variant="text" @tap="toggleCatEdit" col="2" :text="`${editCategory ? 'DONE' : 'RENAME'}` | L" />
columns="*, auto"
v-if="categoriesWithRecipes.length"
>
<Label verticalAlignment="center" col="0" text="Categories" />
<MDButton
variant="text"
@tap="toggleCatEdit"
col="2"
:text="editCategory ? 'DONE' : 'RENAME'"
/>
</GridLayout> </GridLayout>
<ScrollView height="100%"> <ScrollView height="100%">
<StackLayout> <StackLayout>
<GridLayout <GridLayout v-for="(item, index) in categoriesWithRecipes" :key="index" class="sd-item orkm" :class="{
v-for="(item, index) in categoriesWithRecipes"
:key="index"
class="sd-item orkm"
:class="{
'selected-sd-item': currentComponent == item, 'selected-sd-item': currentComponent == item,
}" }" columns="auto, *, auto">
columns="auto, *, auto" <MDRipple row="0" colSpan="3" @tap="navigateTo(item, item, false, true)" />
> <Label col="0" class="bx" :text="icon.label" margin="0 24 0 0" />
<MDRipple <Label col="1" :text="`${item}` | L" />
row="0" <MDButton variant="text" v-if="editCategory" @tap="renameCategory(item)" col="2" class="bx" :text="icon.edit" />
colSpan="3"
@tap="navigateTo(item, false, true)"
/>
<Label
col="0"
class="bx"
:text="icon.label"
margin="0 24 0 0"
/>
<Label col="1" :text="item" />
<MDButton
variant="text"
v-if="editCategory"
@tap="renameCategory(item)"
col="2"
class="bx"
:text="icon.edit"
/>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
</StackLayout> </StackLayout>
<StackLayout row="1"> <StackLayout row="1">
<StackLayout class="hr m-10"></StackLayout> <StackLayout class="hr" margin="0 8 8"></StackLayout>
<GridLayout <GridLayout class="sd-item orkm" :class="{
class="sd-item orkm"
:class="{
'selected-sd-item': currentComponent == item.title, 'selected-sd-item': currentComponent == item.title,
}" }" v-for="(item, index) in bottommenu" :key="index" rows="48" columns="auto, 24, *">
v-for="(item, index) in bottommenu" <MDRipple colSpan="3" @tap="navigateTo(item.component, 'item.title', true, false)" />
:key="index"
rows="48"
columns="auto, 24, *"
>
<MDRipple
colSpan="3"
@tap="navigateTo(item.component, true, false)"
/>
<Label class="bx" col="0" :text="icon[item.icon]" /> <Label class="bx" col="0" :text="icon[item.icon]" />
<Label col="2" :text="item.title" /> <Label col="2" :text="`${item.title}` | L" />
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
<Frame ~mainContent id="main-frame"> <Frame ~mainContent id="main-frame">
<!-- Home --> <EnRecipes ref="enrecipes" :filterFavourites="filterFavourites" :filterTrylater="filterTrylater" :selectedCategory="selectedCategory" :closeDrawer="closeDrawer" :hijackGlobalBackEvent="hijackGlobalBackEvent"
<EnRecipes :releaseGlobalBackEvent="releaseGlobalBackEvent" />
ref="enrecipes"
:filterFavorites="filterFavorites"
:filterTrylater="filterTrylater"
:selectedCategory="selectedCategory"
:closeDrawer="closeDrawer"
:hijackGlobalBackEvent="hijackGlobalBackEvent"
:releaseGlobalBackEvent="releaseGlobalBackEvent"
/>
</Frame> </Frame>
</RadSideDrawer> </RadSideDrawer>
</Page> </Page>
@ -143,11 +62,17 @@ import {
ApplicationSettings, ApplicationSettings,
AndroidApplication, AndroidApplication,
Application, Application,
} from "@nativescript/core" Device
}
from "@nativescript/core"
import Theme from "@nativescript/theme" import Theme from "@nativescript/theme"
import * as Toast from "nativescript-toast" import * as Toast from "nativescript-toast"
import * as application from "tns-core-modules/application" import * as application from "tns-core-modules/application"
import { mapActions, mapState } from "vuex" import {
mapActions,
mapState
}
from "vuex"
import EnRecipes from "./EnRecipes" import EnRecipes from "./EnRecipes"
import MealPlanner from "./MealPlanner" import MealPlanner from "./MealPlanner"
@ -160,11 +85,10 @@ export default {
data() { data() {
return { return {
selectedCategory: null, selectedCategory: null,
filterFavorites: false, filterFavourites: false,
filterTrylater: false, filterTrylater: false,
MealPlanner: MealPlanner, MealPlanner: MealPlanner,
topmenu: [ topmenu: [ {
{
title: "EnRecipes", title: "EnRecipes",
component: "EnRecipes", component: "EnRecipes",
icon: "home", icon: "home",
@ -175,13 +99,12 @@ export default {
icon: "trylater", icon: "trylater",
}, },
{ {
title: "Favorites", title: "Favourites",
component: "Favorites", component: "Favourites",
icon: "heart", icon: "heart",
}, },
], ],
bottommenu: [ bottommenu: [ {
{
title: "Settings", title: "Settings",
component: Settings, component: Settings,
icon: "cog", icon: "cog",
@ -237,14 +160,11 @@ export default {
// HELPERS // HELPERS
toggleCatEdit() { toggleCatEdit() {
this.editCategory = !this.editCategory this.editCategory = !this.editCategory
if (this.selectedCategory) this.setComponent("EnRecipes") if ( this.selectedCategory ) this.setCurrentComponentAction( "EnRecipes" )
this.filterFavorites = this.filterTrylater = false this.filterFavourites = this.filterTrylater = false
this.selectedCategory = null this.selectedCategory = null
this.$refs.enrecipes.updateFilter() this.$refs.enrecipes.updateFilter()
}, },
setComponent(comp) {
this.setCurrentComponentAction(comp)
},
renameCategory( category ) { renameCategory( category ) {
this.releaseGlobalBackEvent() this.releaseGlobalBackEvent()
this.$showModal( PromptDialog, { this.$showModal( PromptDialog, {
@ -256,9 +176,12 @@ export default {
} ).then( ( newCategory ) => { } ).then( ( newCategory ) => {
this.hijackGlobalBackEvent() this.hijackGlobalBackEvent()
if ( newCategory.length ) { if ( newCategory.length ) {
this.renameCategoryAction({ current: category, updated: newCategory }) this.renameCategoryAction( {
current: category,
updated: newCategory
} )
this.editCategory = false this.editCategory = false
this.navigateTo(newCategory, false, true) this.navigateTo( newCategory, newCategory, false, true )
} }
} ) } )
}, },
@ -288,52 +211,52 @@ export default {
function preventDefault() { function preventDefault() {
args.cancel = true args.cancel = true
} }
let vm = this
function isFiltered() {
vm.filterFavorites
? vm.setComponent("Favorites")
: vm.filterTrylater
? vm.setComponent("Try Later")
: vm.selectedCategory
? vm.setComponent(vm.selectedCategory)
: vm.setComponent("EnRecipes")
}
if ( this.$refs.drawer && this.$refs.drawer.nativeView.getIsOpen() ) { if ( this.$refs.drawer && this.$refs.drawer.nativeView.getIsOpen() ) {
preventDefault() preventDefault()
this.closeDrawer() this.closeDrawer()
this.editCategory = false this.editCategory = false
} else if ( }
["Favorites", "Try Later", this.selectedCategory].includes( else if (
[ "Favourites", "Try Later", this.selectedCategory ].includes(
this.currentComponent this.currentComponent
) )
) { ) {
preventDefault() preventDefault()
this.setComponent("EnRecipes") this.setCurrentComponentAction( "EnRecipes" )
this.filterFavorites = this.filterTrylater = false this.filterFavourites = this.filterTrylater = false
this.selectedCategory = null this.selectedCategory = null
this.$refs.enrecipes.updateFilter() this.$refs.enrecipes.updateFilter()
this.releaseGlobalBackEvent() this.releaseGlobalBackEvent()
} }
}, },
navigateTo(to, isTrueComponent, isCategory) { navigateTo( to, title, isTrueComponent, isCategory ) {
if ( title !== this.currentComponent ) {
if ( isTrueComponent ) { if ( isTrueComponent ) {
this.$navigateTo( to, { this.$navigateTo( to, {
frame: "main-frame", frame: "main-frame",
backstackVisible: false, backstackVisible: false
} ) } )
this.closeDrawer() this.closeDrawer()
} else if (!this.editCategory || !isCategory) { }
else if ( !this.editCategory || !isCategory ) {
this.releaseGlobalBackEvent() this.releaseGlobalBackEvent()
this.hijackGlobalBackEvent() this.hijackGlobalBackEvent()
this.setComponent(to) this.setCurrentComponentAction( to )
this.$navigateBack({ frame: "main-frame", backstackVisible: false }) this.$navigateBack( {
this.filterFavorites = to === "Favorites" ? true : false frame: "main-frame",
backstackVisible: false
} )
this.filterFavourites = to === "Favourites" ? true : false
this.filterTrylater = to === "Try Later" ? true : false this.filterTrylater = to === "Try Later" ? true : false
this.selectedCategory = isCategory ? to : null this.selectedCategory = isCategory ? to : null
this.$refs.enrecipes.updateFilter() this.$refs.enrecipes.updateFilter()
this.closeDrawer() this.closeDrawer()
} }
this.editCategory = false this.editCategory = false
}
else {
this.closeDrawer()
}
}, },
}, },
created() { created() {
@ -345,6 +268,7 @@ export default {
if ( !this.categories.length ) this.initializeCategories() if ( !this.categories.length ) this.initializeCategories()
if ( !this.yieldUnits.length ) this.initializeYieldUnits() if ( !this.yieldUnits.length ) this.initializeYieldUnits()
if ( !this.mealPlans.length ) this.initializeMealPlans() if ( !this.mealPlans.length ) this.initializeMealPlans()
console.log( Device.language );
}, },
} }
</script> </script>

View file

@ -2,267 +2,94 @@
<Page @loaded="onPageLoad" @unloaded="releaseBackEvent"> <Page @loaded="onPageLoad" @unloaded="releaseBackEvent">
<ActionBar :androidElevation="viewIsScrolled ? 4 : 0"> <ActionBar :androidElevation="viewIsScrolled ? 4 : 0">
<GridLayout rows="*" columns="auto, *, auto"> <GridLayout rows="*" columns="auto, *, auto">
<MDButton <MDButton variant="text" class="bx" :text="icon.back" automationText="Back" col="0" @tap="navigateBack" />
variant="text" <Label class="title orkm" :text="`${title}` | L" col="1" />
class="bx" <MDButton variant="text" v-if="hasChanges && !saving" class="bx" :text="icon.save" col="2" @tap="saveOperation" />
:text="icon.back"
automationText="Back"
col="0"
@tap="navigateBack"
/>
<Label class="title orkm" :text="title" col="1" />
<MDButton
variant="text"
v-if="hasChanges && !saving"
class="bx"
:text="icon.save"
col="2"
@tap="saveOperation"
/>
<MDActivityIndicator col="2" v-if="saving" :busy="saving" /> <MDActivityIndicator col="2" v-if="saving" :busy="saving" />
</GridLayout> </GridLayout>
</ActionBar> </ActionBar>
<ScrollView width="100%" height="100%" @scroll="onScroll"> <ScrollView width="100%" height="100%" @scroll="onScroll">
<StackLayout width="100%" padding="0 0 88"> <StackLayout width="100%" padding="0 0 88">
<AbsoluteLayout> <AbsoluteLayout>
<StackLayout <StackLayout width="100%" :height="screenWidth" class="imageHolder" verticalAlignment="center">
width="100%" <Image v-if="recipeContent.imageSrc" :src="recipeContent.imageSrc" stretch="aspectFill" width="100%" :height="screenWidth" />
:height="screenWidth" <Label v-else horizontalAlignment="center" class="bx" fontSize="160" :text="icon.image" />
class="imageHolder"
verticalAlignment="center"
>
<Image
v-if="recipeContent.imageSrc"
:src="recipeContent.imageSrc"
stretch="aspectFill"
width="100%"
:height="screenWidth"
/>
<Label
v-else
horizontalAlignment="center"
class="bx"
fontSize="160"
:text="icon.image"
/>
</StackLayout> </StackLayout>
<transition :name="recipeContent.imageSrc ? 'null' : 'bounce'"> <transition :name="recipeContent.imageSrc ? 'null' : 'bounce'">
<MDFloatingActionButton <MDFloatingActionButton v-if="showFab" :top="screenWidth - 44" :left="screenWidth - 88" class="bx" src="res://camera" @tap="imageHandler" />
v-if="showFab"
:top="screenWidth - 44"
:left="screenWidth - 88"
class="bx"
src="res://camera"
@tap="imageHandler"
/>
</transition> </transition>
</AbsoluteLayout> </AbsoluteLayout>
<StackLayout margin="0 16"> <StackLayout margin="0 16">
<AbsoluteLayout class="inputField"> <AbsoluteLayout class="inputField">
<TextField <TextField :hint="'My Healthy Recipe' | L" v-model="recipeContent.title" @loaded="setInputTypeText($event, 'words')" />
hint="My Healthy Recipe" <Label top="0" class="fieldLabel" :text="'Title' | L" />
v-model="recipeContent.title"
@loaded="setInputTypeText($event, 'words')"
/>
<Label top="0" class="fieldLabel" text="Title" />
</AbsoluteLayout> </AbsoluteLayout>
<AbsoluteLayout class="inputField"> <AbsoluteLayout class="inputField">
<TextField <TextField :text="`${recipeContent.category}` | L" editable="false" @focus="modalOpen === false && showCategories(true)" @tap="showCategories(false)" />
:text="recipeContent.category" <Label top="0" class="fieldLabel" :text="'Category' | L" />
editable="false"
@focus="modalOpen === false && showCategories(true)"
@tap="showCategories(false)"
/>
<Label top="0" class="fieldLabel" text="Category" />
</AbsoluteLayout> </AbsoluteLayout>
<GridLayout columns="*, 8, *"> <GridLayout columns="*, 8, *">
<AbsoluteLayout class="inputField" col="0"> <AbsoluteLayout class="inputField" col="0">
<TextField <TextField ref="prepTime" :text="timeRequired('prepTime')" editable="false" @focus="
ref="prepTime"
:text="timeRequired('prepTime')"
editable="false"
@focus="
modalOpen === false && setTimeRequired(true, 'prepTime') modalOpen === false && setTimeRequired(true, 'prepTime')
" " @tap="setTimeRequired(false, 'prepTime')" />
@tap="setTimeRequired(false, 'prepTime')" <Label top="0" class="fieldLabel" :text="'Preparation time' | L" />
/>
<Label top="0" class="fieldLabel" text="Preparation time" />
</AbsoluteLayout> </AbsoluteLayout>
<AbsoluteLayout class="inputField" col="2"> <AbsoluteLayout class="inputField" col="2">
<TextField <TextField ref="cookTime" :text="timeRequired('cookTime')" editable="false" @focus="
ref="cookTime"
:text="timeRequired('cookTime')"
editable="false"
@focus="
modalOpen === false && setTimeRequired(true, 'cookTime') modalOpen === false && setTimeRequired(true, 'cookTime')
" " @tap="setTimeRequired(false, 'cookTime')" />
@tap="setTimeRequired(false, 'cookTime')" <Label top="0" class="fieldLabel" :text="'Cooking time' | L" />
/>
<Label top="0" class="fieldLabel" text="Cooking time" />
</AbsoluteLayout> </AbsoluteLayout>
</GridLayout> </GridLayout>
<GridLayout columns="*, 8, *"> <GridLayout columns="*, 8, *">
<AbsoluteLayout class="inputField" col="0"> <AbsoluteLayout class="inputField" col="0">
<TextField <TextField ref="yieldQuantity" v-model="recipeContent.yield.quantity" hint="1" keyboardType="number" returnKeyType="next" />
ref="yieldQuantity" <Label top="0" class="fieldLabel" :text="'Yield quantity' | L" />
v-model="recipeContent.yield.quantity"
hint="1"
keyboardType="number"
returnKeyType="next"
/>
<Label top="0" class="fieldLabel" text="Yield quantity" />
</AbsoluteLayout> </AbsoluteLayout>
<AbsoluteLayout class="inputField" col="2"> <AbsoluteLayout class="inputField" col="2">
<TextField <TextField :text="`${recipeContent.yield.unit}` | L" editable="false" @focus="modalOpen === false && showYieldUnits(true)" @tap="showYieldUnits(false)" />
:text="recipeContent.yield.unit" <Label top="0" class="fieldLabel" :text="'Yield measured in' | L" />
editable="false"
@focus="modalOpen === false && showYieldUnits(true)"
@tap="showYieldUnits(false)"
/>
<Label top="0" class="fieldLabel" text="Yield measured in" />
</AbsoluteLayout> </AbsoluteLayout>
</GridLayout> </GridLayout>
<StackLayout class="hr" margin="24 16"></StackLayout> <StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout> </StackLayout>
<StackLayout margin="0 16"> <StackLayout margin="0 16">
<Label text="Ingredients" class="sectionTitle" /> <Label :text="'Ingredients' | L" class="sectionTitle" />
<GridLayout <GridLayout columns="*,8,auto,8,auto,8,auto" v-for="(ingredient, index) in recipeContent.ingredients" :key="index">
columns="*,8,auto,8,auto,8,auto" <TextField ref="ingredient" @loaded="focusField($event, 'sentence')" col="0" v-model="recipeContent.ingredients[index].item" :hint="`${$options.filters.L('Item')} ${index + 1}`" returnKeyType="next" />
v-for="(ingredient, index) in recipeContent.ingredients" <TextField width="60" col="2" v-model="recipeContent.ingredients[index].quantity" hint="1.00" keyboardType="number" returnKeyType="next" />
:key="index" <TextField width="76" col="4" :text="`${recipeContent.ingredients[index].unit}` | L" editable="false" @focus="modalOpen === false && showUnits($event, true, index)" @tap="showUnits($event, false, index)" />
> <MDButton variant="text" col="6" class="bx closeBtn" :text="icon.close" @tap="removeIngredient(index)" />
<TextField
ref="ingredient"
@loaded="focusField($event, 'sentence')"
col="0"
v-model="recipeContent.ingredients[index].item"
:hint="`Item ${index + 1}`"
returnKeyType="next"
/>
<TextField
width="60"
col="2"
v-model="recipeContent.ingredients[index].quantity"
hint="1.00"
keyboardType="number"
returnKeyType="next"
/>
<TextField
width="76"
col="4"
v-model="recipeContent.ingredients[index].unit"
hint="Unit"
editable="false"
@focus="modalOpen === false && showUnits($event, true, index)"
@tap="showUnits($event, false, index)"
/>
<MDButton
variant="text"
col="6"
class="bx closeBtn"
:text="icon.close"
@tap="removeIngredient(index)"
/>
</GridLayout> </GridLayout>
<MDButton <MDButton variant="text" class="text-btn orkm" :text="`+ ${$options.filters.L('ADD INGREDIENT')}`" @tap="addIngredient()" />
variant="text"
class="text-btn orkm"
text="+ ADD INGREDIENT"
@tap="addIngredient()"
/>
<StackLayout class="hr" margin="24 16"></StackLayout> <StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout> </StackLayout>
<StackLayout margin="0 16"> <StackLayout margin="0 16">
<Label text="Instructions" class="sectionTitle" /> <Label :text="'Instructions' | L" class="sectionTitle" />
<GridLayout <GridLayout columns="*,8,auto" v-for="(instruction, index) in recipeContent.instructions" :key="index">
columns="*,8,auto" <TextView @loaded="focusField($event, 'multiLine')" col="0" :hint="`${$options.filters.L('Step')} ${index + 1}`" v-model="recipeContent.instructions[index]" />
v-for="(instruction, index) in recipeContent.instructions" <MDButton variant="text" col="2" class="bx closeBtn" :text="icon.close" @tap="removeInstruction(index)" />
:key="index"
>
<TextView
@loaded="focusField($event, 'multiLine')"
col="0"
:hint="`Step ${index + 1}`"
v-model="recipeContent.instructions[index]"
/>
<MDButton
variant="text"
col="2"
class="bx closeBtn"
:text="icon.close"
@tap="removeInstruction(index)"
/>
</GridLayout> </GridLayout>
<MDButton <MDButton variant="text" class="text-btn orkm" :text="`+ ${$options.filters.L('ADD STEP')}`" @tap="addInstruction" />
variant="text"
class="text-btn orkm"
text="+ ADD STEP"
@tap="addInstruction"
/>
<StackLayout class="hr" margin="24 16"></StackLayout> <StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout> </StackLayout>
<StackLayout margin="0 16"> <StackLayout margin="0 16">
<Label text="Combinations" class="sectionTitle" /> <Label :text="'Combinations' | L" class="sectionTitle" />
<GridLayout <GridLayout columns="*,8,auto" v-for="(combination, index) in recipeContent.combinations" :key="index">
columns="*,8,auto" <TextField class="combinationToken" col="0" :text="getCombinationTitle(combination)" editable="false" />
v-for="(combination, index) in recipeContent.combinations" <MDButton variant="text" col="2" class="bx closeBtn" :text="icon.close" @tap="removeCombination(combination)" />
:key="index"
>
<TextField
class="combinationToken"
col="0"
:text="getCombinationTitle(combination)"
editable="false"
/>
<MDButton
variant="text"
col="2"
class="bx closeBtn"
:text="icon.close"
@tap="removeCombination(combination)"
/>
</GridLayout> </GridLayout>
<MDButton <MDButton variant="text" class="text-btn orkm" :text="`+ ${$options.filters.L('ADD COMBINATION')}`" @tap="showCombinations" />
variant="text"
class="text-btn orkm"
text="+ ADD COMBINATION"
@tap="showCombinations"
/>
<StackLayout class="hr" margin="24 16"></StackLayout> <StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout> </StackLayout>
<StackLayout margin="0 16"> <StackLayout margin="0 16">
<Label text="Notes" class="sectionTitle" /> <Label :text="'Notes' | L" class="sectionTitle" />
<GridLayout <GridLayout columns="*,8,auto" v-for="(note, index) in recipeContent.notes" :key="index">
columns="*,8,auto" <TextView @loaded="focusField($event, 'multiLine')" col="0" :hint="`${$options.filters.L('Note')} ${index + 1}`" v-model="recipeContent.notes[index]" />
v-for="(note, index) in recipeContent.notes" <MDButton variant="text" col="2" class="bx closeBtn" :text="icon.close" @tap="removeNote(index)" />
:key="index"
>
<TextView
@loaded="focusField($event, 'multiLine')"
col="0"
:hint="`Note ${index + 1}`"
v-model="recipeContent.notes[index]"
/>
<MDButton
variant="text"
col="2"
class="bx closeBtn"
:text="icon.close"
@tap="removeNote(index)"
/>
</GridLayout> </GridLayout>
<MDButton <MDButton variant="text" class="text-btn orkm" :text="`+ ${$options.filters.L('ADD NOTE')}`" @tap="addNote" />
variant="text"
class="text-btn orkm"
text="+ ADD NOTE"
@tap="addNote"
/>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
@ -281,31 +108,34 @@ import {
path, path,
Screen, Screen,
Utils, Utils,
} from "@nativescript/core" Observable
}
from "@nativescript/core"
import * as Permissions from "@nativescript-community/perms" import * as Permissions from "@nativescript-community/perms"
import * as Toast from "nativescript-toast" import * as Toast from "nativescript-toast"
import * as Filepicker from "nativescript-plugin-filepicker" import * as Filepicker from "nativescript-plugin-filepicker"
import { ImageCropper } from "nativescript-imagecropper" import {
import { mapState, mapActions } from "vuex" ImageCropper
}
from "nativescript-imagecropper"
import {
localize
}
from "@nativescript/localize"
import {
mapState,
mapActions
}
from "vuex"
import ViewRecipe from "./ViewRecipe.vue" import ViewRecipe from "./ViewRecipe.vue"
import ActionDialog from "./modal/ActionDialog.vue" import ActionDialog from "./modal/ActionDialog.vue"
import ActionDialogWithSearch from "./modal/ActionDialogWithSearch.vue" import ActionDialogWithSearch from "./modal/ActionDialogWithSearch.vue"
import ConfirmDialog from "./modal/ConfirmDialog.vue" import ConfirmDialog from "./modal/ConfirmDialog.vue"
import PromptDialog from "./modal/PromptDialog.vue" import PromptDialog from "./modal/PromptDialog.vue"
import ListPicker from "./modal/ListPicker.vue" import ListPicker from "./modal/ListPicker.vue"
import * as utils from "~/shared/utils" import * as utils from "~/shared/utils"
export default { export default {
props: [ props: [ "recipeID", "selectedCategory", "filterFavourites", "filterTrylater", "navigationFromView", ],
"recipeID",
"selectedCategory",
"filterFavorites",
"filterTrylater",
"navigationFromView",
],
data() { data() {
return { return {
title: "New recipe", title: "New recipe",
@ -318,7 +148,7 @@ export default {
cookTime: "00:00", cookTime: "00:00",
yield: { yield: {
quantity: undefined, quantity: undefined,
unit: "Servings", unit: "Serving",
}, },
ingredients: [], ingredients: [],
instructions: [], instructions: [],
@ -340,14 +170,7 @@ export default {
} }
}, },
computed: { computed: {
...mapState([ ...mapState( [ "icon", "units", "yieldUnits", "recipes", "categories", "currentComponent", ] ),
"icon",
"units",
"yieldUnits",
"recipes",
"categories",
"currentComponent",
]),
screenWidth() { screenWidth() {
return Screen.mainScreen.widthDIPs return Screen.mainScreen.widthDIPs
}, },
@ -355,31 +178,24 @@ export default {
return Application.systemAppearance() return Application.systemAppearance()
}, },
hasChanges() { hasChanges() {
return ( return ( JSON.stringify( this.recipeContent ) !== JSON.stringify( this.tempRecipeContent ) )
JSON.stringify(this.recipeContent) !==
JSON.stringify(this.tempRecipeContent)
)
}, },
}, },
methods: { methods: {
...mapActions([ ...mapActions( [ "setCurrentComponentAction", "addRecipeAction", "overwriteRecipeAction", "addCategoryAction", "addYieldUnitAction", "unSyncCombinationsAction", ] ),
"setCurrentComponentAction", onPageLoad( args ) {
"addRecipeAction", const page = args.object;
"overwriteRecipeAction", page.bindingContext = new Observable();
"addCategoryAction",
"addYieldUnitAction",
"unSyncCombinationsAction",
]),
onPageLoad() {
this.showFab = true this.showFab = true
}, },
timeRequired( time ) { timeRequired( time ) {
let t = this.recipeContent[ time ].split( ":" ) let t = this.recipeContent[ time ].split( ":" )
let h = parseInt( t[ 0 ] ) let h = parseInt( t[ 0 ] )
let m = parseInt( t[ 1 ] ) let m = parseInt( t[ 1 ] )
return h ? (m ? `${h} hr ${m} min` : `${h} hr`) : `${m} min` let hr = localize( 'hr' )
let min = localize( 'min' )
return h ? ( m ? `${h} ${hr} ${m} ${min}` : `${h} ${hr}` ) : `${m} ${min}`
}, },
// HELPERS // HELPERS
focusField( args, type ) { focusField( args, type ) {
if ( type ) this.setInputTypeText( args, type ) if ( type ) this.setInputTypeText( args, type )
@ -392,26 +208,13 @@ export default {
let field = args.object let field = args.object
switch ( type ) { switch ( type ) {
case "words": case "words":
field.android.setInputType( field.android.setInputType( android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS | android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT )
android.text.InputType.TYPE_CLASS_TEXT |
android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS |
android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
)
break break
case "sentence": case "sentence":
field.android.setInputType( field.android.setInputType( android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT )
android.text.InputType.TYPE_CLASS_TEXT |
android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES |
android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
)
break break
case "multiLine": case "multiLine":
field.android.setInputType( field.android.setInputType( android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT )
android.text.InputType.TYPE_CLASS_TEXT |
android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE |
android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES |
android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
)
break break
default: default:
break break
@ -459,7 +262,6 @@ export default {
onScroll( args ) { onScroll( args ) {
this.viewIsScrolled = args.scrollY ? true : false this.viewIsScrolled = args.scrollY ? true : false
}, },
// DATA LIST // DATA LIST
showCategories( focus ) { showCategories( focus ) {
this.modalOpen = true this.modalOpen = true
@ -468,7 +270,6 @@ export default {
props: { props: {
title: "Category", title: "Category",
list: [ ...this.categories ], list: [ ...this.categories ],
// height: "420",
action: "ADD NEW", action: "ADD NEW",
}, },
} ).then( ( action ) => { } ).then( ( action ) => {
@ -487,12 +288,14 @@ export default {
if ( focus ) this.autoFocusField( "prepTime", false ) if ( focus ) this.autoFocusField( "prepTime", false )
} }
} ) } )
} else if (action) { }
else if ( action ) {
this.recipeContent.category = action this.recipeContent.category = action
this.hijackBackEvent() this.hijackBackEvent()
this.modalOpen = false this.modalOpen = false
if ( focus ) this.autoFocusField( "prepTime", false ) if ( focus ) this.autoFocusField( "prepTime", false )
} else { }
else {
this.hijackBackEvent() this.hijackBackEvent()
} }
} ) } )
@ -531,12 +334,14 @@ export default {
if ( focus ) this.addIngredient() if ( focus ) this.addIngredient()
} }
} ) } )
} else if (action) { }
else if ( action ) {
this.recipeContent.yield.unit = action this.recipeContent.yield.unit = action
this.hijackBackEvent() this.hijackBackEvent()
this.modalOpen = false this.modalOpen = false
if ( focus ) this.addIngredient() if ( focus ) this.addIngredient()
} else { }
else {
this.hijackBackEvent() this.hijackBackEvent()
} }
} ) } )
@ -546,7 +351,7 @@ export default {
this.releaseBackEvent() this.releaseBackEvent()
this.$showModal( ActionDialog, { this.$showModal( ActionDialog, {
props: { props: {
title: "Unit", title: localize( "Unit" ),
list: [ ...this.units ], list: [ ...this.units ],
}, },
} ).then( ( action ) => { } ).then( ( action ) => {
@ -557,21 +362,16 @@ export default {
if ( focus ) { if ( focus ) {
if ( this.recipeContent.ingredients.length - 1 === index ) { if ( this.recipeContent.ingredients.length - 1 === index ) {
this.addIngredient() this.addIngredient()
} else { }
else {
this.$refs.ingredient[ index + 1 ].nativeView.focus() this.$refs.ingredient[ index + 1 ].nativeView.focus()
setTimeout( setTimeout(
(e) => ( e ) => Utils.ad.showSoftInput( this.$refs.ingredient[ index + 1 ].nativeView.android ), 10 )
Utils.ad.showSoftInput(
this.$refs.ingredient[index + 1].nativeView.android
),
10
)
} }
} }
} }
} ) } )
}, },
// NAVIGATION HANDLERS // NAVIGATION HANDLERS
navigateBackController() { navigateBackController() {
if ( this.navigationFromView ) { if ( this.navigationFromView ) {
@ -582,7 +382,8 @@ export default {
}, },
backstackVisible: false, backstackVisible: false,
} ) } )
} else this.$navigateBack() }
else this.$navigateBack()
}, },
navigateBack() { navigateBack() {
if ( this.hasChanges ) { if ( this.hasChanges ) {
@ -590,8 +391,7 @@ export default {
this.$showModal( ConfirmDialog, { this.$showModal( ConfirmDialog, {
props: { props: {
title: "Unsaved changes", title: "Unsaved changes",
description: description: localize( "Are you sure you want to discard unsaved changes to this recipe?" ),
"Are you sure you want to discard unsaved changes to this recipe?",
cancelButtonText: "DISCARD", cancelButtonText: "DISCARD",
okButtonText: "KEEP EDITING", okButtonText: "KEEP EDITING",
}, },
@ -602,22 +402,17 @@ export default {
this.releaseBackEvent() this.releaseBackEvent()
} }
} ) } )
} else { }
else {
this.navigateBackController() this.navigateBackController()
this.releaseBackEvent() this.releaseBackEvent()
} }
}, },
hijackBackEvent() { hijackBackEvent() {
AndroidApplication.on( AndroidApplication.on( AndroidApplication.activityBackPressedEvent, this.backEvent )
AndroidApplication.activityBackPressedEvent,
this.backEvent
)
}, },
releaseBackEvent() { releaseBackEvent() {
AndroidApplication.off( AndroidApplication.off( AndroidApplication.activityBackPressedEvent, this.backEvent )
AndroidApplication.activityBackPressedEvent,
this.backEvent
)
}, },
backEvent( args ) { backEvent( args ) {
if ( this.hasChanges && !this.blockModal ) { if ( this.hasChanges && !this.blockModal ) {
@ -625,7 +420,6 @@ export default {
this.navigateBack() this.navigateBack()
} }
}, },
// DATA HANDLERS // DATA HANDLERS
imageHandler() { imageHandler() {
if ( this.recipeContent.imageSrc ) { if ( this.recipeContent.imageSrc ) {
@ -640,12 +434,14 @@ export default {
this.blockModal = false this.blockModal = false
if ( action ) { if ( action ) {
this.permissionCheck( this.permissionConfirmation, this.imagePicker ) this.permissionCheck( this.permissionConfirmation, this.imagePicker )
} else if (action != null) { }
else if ( action != null ) {
this.recipeContent.imageSrc = null this.recipeContent.imageSrc = null
this.releaseBackEvent() this.releaseBackEvent()
} }
} ) } )
} else { }
else {
this.permissionCheck( this.permissionConfirmation, this.imagePicker ) this.permissionCheck( this.permissionConfirmation, this.imagePicker )
} }
}, },
@ -653,8 +449,7 @@ export default {
return this.$showModal( ConfirmDialog, { return this.$showModal( ConfirmDialog, {
props: { props: {
title: "Grant permission", title: "Grant permission",
description: description: localize( "EnRecipes requires storage permission in order to set recipe photo." ),
"EnRecipes requires storage permission in order to set recipe photo.",
cancelButtonText: "NOT NOW", cancelButtonText: "NOT NOW",
okButtonText: "CONTINUE", okButtonText: "CONTINUE",
}, },
@ -673,7 +468,7 @@ export default {
ApplicationSettings.setBoolean( "storagePermissionAsked", true ) ApplicationSettings.setBoolean( "storagePermissionAsked", true )
break break
case "denied": case "denied":
Toast.makeText("Permission denied").show() Toast.makeText( localize( "Permission denied" ) ).show()
break break
default: default:
break break
@ -681,54 +476,39 @@ export default {
} ) } )
} }
} ) } )
} else { }
else {
Permissions.check( "photo" ).then( ( res ) => { Permissions.check( "photo" ).then( ( res ) => {
res[0] !== "authorized" res[ 0 ] !== "authorized" ? confirmation().then( ( e ) => e && utils.openAppSettingsPage() ) : action()
? confirmation().then((e) => e && utils.openAppSettingsPage())
: action()
} ) } )
} }
}, },
imagePicker() { imagePicker() {
ApplicationSettings.setBoolean( "storagePermissionAsked", true ) ApplicationSettings.setBoolean( "storagePermissionAsked", true )
this.cacheImagePath = path.join( this.cacheImagePath = path.join( knownFolders.temp().path, `${this.getRandomID()}.jpg` )
knownFolders.temp().path,
`${this.getRandomID()}.jpg`
)
Filepicker.create( { Filepicker.create( {
mode: "single", mode: "single",
extensions: [ "png", "jpeg", "jpg" ], extensions: [ "png", "jpeg", "jpg" ],
}) } ).present().then( ( selection ) => {
.present()
.then((selection) => {
let imgPath = selection[ 0 ] let imgPath = selection[ 0 ]
ImageSource.fromFile( imgPath ).then( ( image ) => { ImageSource.fromFile( imgPath ).then( ( image ) => {
ImageCropper.prototype ImageCropper.prototype.show( image, {
.show(
image,
{
width: 1080, width: 1080,
height: 1080, height: 1080,
}, }, {
{
hideBottomControls: true, hideBottomControls: true,
toolbarTitle: "Crop photo", toolbarTitle: localize( "Crop photo" ),
statusBarColor: "#ff5200", statusBarColor: "#ff5200",
toolbarTextColor: toolbarTextColor: this.appTheme == "light" ? "#212529" : "#f1f3f5",
this.appTheme == "light" ? "#212529" : "#f1f3f5", toolbarColor: this.appTheme == "light" ? "#f1f3f5" : "#212529",
toolbarColor:
this.appTheme == "light" ? "#f1f3f5" : "#212529",
cropFrameColor: "#ff5200", cropFrameColor: "#ff5200",
} } ).then( ( cropped ) => {
)
.then((cropped) => {
cropped.image.saveToFile( this.cacheImagePath, "jpg", 75 ) cropped.image.saveToFile( this.cacheImagePath, "jpg", 75 )
this.recipeContent.imageSrc = this.cacheImagePath this.recipeContent.imageSrc = this.cacheImagePath
} ) } )
} ) } )
} ) } )
}, },
// INPUT FIELD HANDLERS // INPUT FIELD HANDLERS
fieldDeletionConfirm( title ) { fieldDeletionConfirm( title ) {
return this.$showModal( ConfirmDialog, { return this.$showModal( ConfirmDialog, {
@ -748,36 +528,34 @@ export default {
}, },
removeIngredient( index ) { removeIngredient( index ) {
if ( this.recipeContent.ingredients[ index ].item.length ) { if ( this.recipeContent.ingredients[ index ].item.length ) {
this.fieldDeletionConfirm("Remove ingredient").then((res) => { this.fieldDeletionConfirm( "Remove ingredient?" ).then( ( res ) => {
if ( res ) { if ( res ) {
this.recipeContent.ingredients.splice( index, 1 ) this.recipeContent.ingredients.splice( index, 1 )
} }
} ) } )
} else this.recipeContent.ingredients.splice(index, 1) }
else this.recipeContent.ingredients.splice( index, 1 )
}, },
addInstruction() { addInstruction() {
this.recipeContent.instructions.push( "" ) this.recipeContent.instructions.push( "" )
}, },
removeInstruction( index ) { removeInstruction( index ) {
if ( this.recipeContent.instructions[ index ].length ) { if ( this.recipeContent.instructions[ index ].length ) {
this.fieldDeletionConfirm("Remove instruction").then((res) => { this.fieldDeletionConfirm( "Remove instruction?" ).then( ( res ) => {
res && this.recipeContent.instructions.splice( index, 1 ) res && this.recipeContent.instructions.splice( index, 1 )
} ) } )
} else this.recipeContent.instructions.splice(index, 1) }
else this.recipeContent.instructions.splice( index, 1 )
}, },
getCombinationTitle( id ) { getCombinationTitle( id ) {
return this.recipes.filter( ( e ) => e.id === id )[ 0 ].title return this.recipes.filter( ( e ) => e.id === id )[ 0 ].title
}, },
showCombinations() { showCombinations() {
let existingCombinations = [ let existingCombinations = [ ...this.recipeContent.combinations,
...this.recipeContent.combinations,
this.recipeContent.id, this.recipeContent.id,
] ]
let filteredRecipes = this.recipes.filter( let filteredRecipes = this.recipes.filter(
(e) => !existingCombinations.includes(e.id) ( e ) => !existingCombinations.includes( e.id ) )
)
this.$showModal( ActionDialogWithSearch, { this.$showModal( ActionDialogWithSearch, {
props: { props: {
title: "Select a recipe", title: "Select a recipe",
@ -791,35 +569,32 @@ export default {
}, },
removeCombination( id ) { removeCombination( id ) {
let index = this.recipeContent.combinations.indexOf( id ) let index = this.recipeContent.combinations.indexOf( id )
this.fieldDeletionConfirm("Remove combination").then((res) => { this.fieldDeletionConfirm( "Remove combination?" ).then( res => {
if ( res ) { if ( res ) {
this.recipeContent.combinations.splice( index, 1 ) this.recipeContent.combinations.splice( index, 1 )
this.unSyncCombinations.push( id ) this.unSyncCombinations.push( id )
} }
} ) } )
}, },
addNote() { addNote() {
this.recipeContent.notes.push( "" ) this.recipeContent.notes.push( "" )
}, },
removeNote( index ) { removeNote( index ) {
if ( this.recipeContent.notes[ index ].length ) { if ( this.recipeContent.notes[ index ].length ) {
this.fieldDeletionConfirm("Remove note").then((res) => { this.fieldDeletionConfirm( "Remove 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) }
else this.recipeContent.notes.splice( index, 1 )
}, },
// SAVE OPERATION // SAVE OPERATION
clearEmptyFields() { clearEmptyFields() {
if (!this.recipeContent.title) if ( !this.recipeContent.title ) this.recipeContent.title = "Untitled Recipe"
this.recipeContent.title = "Untitled Recipe" if ( !this.recipeContent.yield.quantity ) this.recipeContent.yield.quantity = 1
if (!this.recipeContent.yield.quantity)
this.recipeContent.yield.quantity = 1
this.recipeContent.ingredients = this.recipeContent.ingredients.filter( this.recipeContent.ingredients = this.recipeContent.ingredients.filter(
(e) => e.item ( e ) => e.item )
)
let vm = this let vm = this
function clearEmpty( arr ) { function clearEmpty( arr ) {
vm.recipeContent[ arr ] = vm.recipeContent[ arr ].filter( ( e ) => e ) vm.recipeContent[ arr ] = vm.recipeContent[ arr ].filter( ( e ) => e )
} }
@ -831,26 +606,18 @@ export default {
this.clearEmptyFields() this.clearEmptyFields()
this.recipeContent.lastModified = new Date() this.recipeContent.lastModified = new Date()
if ( this.cacheImagePath ) { if ( this.cacheImagePath ) {
let recipeImage = path.join( let recipeImage = path.join( knownFolders.documents().getFolder( "EnRecipes" ).getFolder( "Images" ).path, `${this.getRandomID()}.jpg` )
knownFolders
.documents()
.getFolder("EnRecipes")
.getFolder("Images").path,
`${this.getRandomID()}.jpg`
)
let binarySource = File.fromPath( this.cacheImagePath ).readSync() let binarySource = File.fromPath( this.cacheImagePath ).readSync()
File.fromPath( recipeImage ).writeSync( binarySource ) File.fromPath( recipeImage ).writeSync( binarySource )
this.recipeContent.imageSrc = recipeImage this.recipeContent.imageSrc = recipeImage
knownFolders.temp().clear() knownFolders.temp().clear()
} }
if ( this.recipeContent.imageSrc ) { if ( this.recipeContent.imageSrc ) {
if ( if ( this.tempRecipeContent.imageSrc && this.tempRecipeContent.imageSrc !== this.recipeContent.imageSrc ) {
this.tempRecipeContent.imageSrc &&
this.tempRecipeContent.imageSrc !== this.recipeContent.imageSrc
) {
getFileAccess().deleteFile( this.tempRecipeContent.imageSrc ) getFileAccess().deleteFile( this.tempRecipeContent.imageSrc )
} }
} else if (this.tempRecipeContent.imageSrc) { }
else if ( this.tempRecipeContent.imageSrc ) {
getFileAccess().deleteFile( this.tempRecipeContent.imageSrc ) getFileAccess().deleteFile( this.tempRecipeContent.imageSrc )
} }
this.unSyncCombinationsAction( { this.unSyncCombinationsAction( {
@ -865,7 +632,8 @@ export default {
id: this.recipeID, id: this.recipeID,
recipe: this.recipeContent, recipe: this.recipeContent,
} ) } )
} else { }
else {
this.recipeContent.id = this.newRecipeID this.recipeContent.id = this.newRecipeID
this.addRecipeAction( { this.addRecipeAction( {
id: this.newRecipeID, id: this.newRecipeID,
@ -883,23 +651,15 @@ export default {
this.setCurrentComponentAction( "EditRecipe" ) this.setCurrentComponentAction( "EditRecipe" )
}, 500 ) }, 500 )
this.title = this.recipeID ? "Edit recipe" : "New recipe" this.title = this.recipeID ? "Edit recipe" : "New recipe"
if ( this.recipeID ) { if ( this.recipeID ) {
let recipe = this.recipes.filter( ( e ) => e.id === this.recipeID )[ 0 ] let recipe = this.recipes.filter( ( e ) => e.id === this.recipeID )[ 0 ]
Object.assign( this.recipeContent, JSON.parse( JSON.stringify( recipe ) ) ) Object.assign( this.recipeContent, JSON.parse( JSON.stringify( recipe ) ) )
Object.assign( Object.assign( this.tempRecipeContent, JSON.parse( JSON.stringify( this.recipeContent ) ) )
this.tempRecipeContent, }
JSON.parse(JSON.stringify(this.recipeContent)) else {
) if ( this.selectedCategory ) this.recipeContent.category = this.selectedCategory
} else { if ( this.filterFavourites ) this.recipeContent.isFavorite = true
if (this.selectedCategory) Object.assign( this.tempRecipeContent, JSON.parse( JSON.stringify( this.recipeContent ) ) )
this.recipeContent.category = this.selectedCategory
if (this.filterFavorites) this.recipeContent.isFavorite = true
Object.assign(
this.tempRecipeContent,
JSON.parse(JSON.stringify(this.recipeContent))
)
this.newRecipeID = this.getRandomID() this.newRecipeID = this.getRandomID()
} }
this.hijackBackEvent() this.hijackBackEvent()

View file

@ -1,111 +1,37 @@
<template> <template>
<Page @loaded="onPageLoad"> <Page @loaded="onPageLoad" @unloaded="onPageUnload">
<ActionBar :androidElevation="viewIsScrolled ? 4 : 0"> <ActionBar :androidElevation="viewIsScrolled ? 4 : 0">
<!-- Search Actionbar --> <GridLayout v-if="showSearch" columns="auto, *" verticalAlignment="center">
<GridLayout <MDButton class="bx" :text="icon.back" variant="text" automationText="Back" col="0" @tap="closeSearch" />
v-if="showSearch" <SearchBar col="1" :hint="'Search' | L" v-model="searchQuery" @textChange="updateFilter" @clear="clearSearch" />
columns="auto, *"
verticalAlignment="center"
>
<MDButton
class="bx"
:text="icon.back"
variant="text"
automationText="Back"
col="0"
@tap="closeSearch"
/>
<SearchBar
col="1"
hint="Search"
v-model="searchQuery"
@textChange="updateFilter"
@clear="clearSearch"
/>
</GridLayout> </GridLayout>
<!-- Home Actionbar -->
<GridLayout v-else columns="auto, *, auto, auto"> <GridLayout v-else columns="auto, *, auto, auto">
<MDButton <MDButton class="bx" col="0" variant="text" @tap="showDrawer" :text="icon.menu" automationText="Back" />
class="bx" <Label class="title orkm" :text="`${currentComponent}` | L" col="1" />
col="0" <MDButton v-if="recipes.length" class="bx" :text="icon.search" variant="text" col="2" @tap="openSearch" />
variant="text" <MDButton v-if="recipes.length" class="bx" :text="icon.sort" variant="text" col="3" @tap="sortDialog" />
@tap="showDrawer"
:text="icon.menu"
automationText="Back"
/>
<Label class="title orkm" :text="currentComponent" col="1" />
<MDButton
v-if="recipes.length"
class="bx"
:text="icon.search"
variant="text"
col="2"
@tap="openSearch"
/>
<MDButton
v-if="recipes.length"
class="bx"
:text="icon.sort"
variant="text"
col="3"
@tap="sortDialog"
/>
</GridLayout> </GridLayout>
</ActionBar> </ActionBar>
<AbsoluteLayout> <AbsoluteLayout>
<RadListView <RadListView ref="listView" itemHeight="112" for="recipe in recipes" swipeActions="true" @itemSwipeProgressChanged="onSwiping" @itemSwipeProgressEnded="onSwipeEnded" @scrolled="onScroll" :filteringFunction="filterFunction"
ref="listView" :sortingFunction="sortFunction">
itemHeight="112"
for="recipe in recipes"
swipeActions="true"
@itemSwipeProgressChanged="onSwiping"
@itemSwipeProgressEnded="onSwipeEnded"
@scrolled="onScroll"
:filteringFunction="filterFunction"
:sortingFunction="sortFunction"
>
<v-template> <v-template>
<GridLayout <GridLayout class="recipeItem" rows="112" columns="112, *" androidElevation="1">
class="recipeItem"
rows="112"
columns="112, *"
androidElevation="1"
>
<MDRipple colSpan="2" @tap="viewRecipe(recipe.id)" /> <MDRipple colSpan="2" @tap="viewRecipe(recipe.id)" />
<GridLayout class="imageHolder card" rows="112" columns="112"> <GridLayout class="imageHolder card" rows="112" columns="112">
<Image <Image row="0" col="0" v-if="recipe.imageSrc" :src="recipe.imageSrc" stretch="aspectFill" decodeWidth="112" decodeHeight="112" loadMode="async" />
row="0" <Label v-else row="0" col="0" horizontalAlignment="center" class="bx" fontSize="56" :text="icon.image" />
col="0"
v-if="recipe.imageSrc"
:src="recipe.imageSrc"
stretch="aspectFill"
decodeWidth="112"
decodeHeight="112"
loadMode="async"
/>
<Label
v-else
row="0"
col="0"
horizontalAlignment="center"
class="bx"
fontSize="56"
:text="icon.image"
/>
</GridLayout> </GridLayout>
<StackLayout class="recipeInfo" col="1"> <StackLayout class="recipeInfo" col="1">
<Label :text="recipe.category" class="orkm category" /> <Label :text="`${recipe.category}` | L" class="orkm category" />
<Label :text="recipe.title" class="orkm title" /> <Label :text="recipe.title" class="orkm title" />
<StackLayout class="timeContainer" orientation="horizontal"> <StackLayout class="timeContainer" orientation="horizontal">
<Label class="bx small" :text="icon.time" /> <Label class="bx small" :text="icon.time" />
<Label <Label class="time" :text="
class="time"
:text="
`${ `${
formattedTotalTime(recipe.prepTime, recipe.cookTime).time formattedTotalTime(recipe.prepTime, recipe.cookTime).time
}` }`
" " />
/>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
@ -122,95 +48,44 @@
</v-template> </v-template>
</RadListView> </RadListView>
<GridLayout rows="*, auto, *, 88" columns="*" class="emptyStateContainer"> <GridLayout rows="*, auto, *, 88" columns="*" class="emptyStateContainer">
<StackLayout <StackLayout row="1" class="emptyState" v-if="
row="1"
class="emptyState"
v-if="
!recipes.length && !recipes.length &&
!filterFavorites && !filterFavourites &&
!filterTrylater && !filterTrylater &&
!selectedCategory !selectedCategory
" " @tap="addRecipe">
@tap="addRecipe"
>
<Label class="bx icon" :text="icon.plusCircle" /> <Label class="bx icon" :text="icon.plusCircle" />
<Label <Label class="title orkm" :text="'Start adding your recipes!' | L" textWrap="true" />
class="title orkm"
text="Start adding your recipes!"
textWrap="true"
/>
<StackLayout orientation="horizontal" horizontalAlignment="center"> <StackLayout orientation="horizontal" horizontalAlignment="center">
<Label text="Use the " /> <Label :text="'Use the plus button to add one' | L" textWrap="true" />
<Label class="bx" :text="icon.plus" />
<Label text=" button to add one" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<StackLayout <StackLayout row="1" class="emptyState" v-if="!filteredRecipes.length && filterTrylater && !searchQuery">
row="1"
class="emptyState"
v-if="!filteredRecipes.length && filterTrylater && !searchQuery"
>
<Label class="bx icon" :text="icon.trylaterOutline" textWrap="true" /> <Label class="bx icon" :text="icon.trylaterOutline" textWrap="true" />
<Label class="title orkm" text="All done!" textWrap="true" /> <Label class="title orkm" :text="'All done!' | L" textWrap="true" />
<Label <Label :text="'Recipes you mark as try later will be listed here' | L" textWrap="true" />
text="Recipes you mark as try later will be listed here"
textWrap="true"
/>
</StackLayout> </StackLayout>
<StackLayout <StackLayout row="1" class="emptyState" v-if="!filteredRecipes.length && filterFavourites && !searchQuery">
row="1"
class="emptyState"
v-if="!filteredRecipes.length && filterFavorites && !searchQuery"
>
<Label class="bx icon" :text="icon.heartOutline" textWrap="true" /> <Label class="bx icon" :text="icon.heartOutline" textWrap="true" />
<Label class="title orkm" text="No favorites yet" textWrap="true" /> <Label class="title orkm" :text="'No favourites yet' | L" textWrap="true" />
<Label <Label :text="'Recipes you mark as favourite will be listed here' | L" textWrap="true" />
text="Recipes you mark as favorite will be listed here"
textWrap="true"
/>
</StackLayout> </StackLayout>
<StackLayout <StackLayout row="1" class="emptyState" v-if="!filteredRecipes.length && !searchQuery && selectedCategory">
row="1"
class="emptyState"
v-if="!filteredRecipes.length && !searchQuery && selectedCategory"
>
<Label class="bx icon" :text="icon.labelOutline" textWrap="true" /> <Label class="bx icon" :text="icon.labelOutline" textWrap="true" />
<Label <Label class="title orkm" :text="'Category looks empty' | L" textWrap="true" />
class="title orkm"
text="Category looks empty"
textWrap="true"
/>
<StackLayout orientation="horizontal" horizontalAlignment="center"> <StackLayout orientation="horizontal" horizontalAlignment="center">
<Label text="Use the " textWrap="true" /> <Label :text="'Use the plus button to add one' | L" textWrap="true" />
<Label class="bx" :text="icon.plus" />
<Label text=" button to add a recipe" textWrap="true" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<StackLayout <StackLayout row="1" class="emptyState" v-if="!filteredRecipes.length && searchQuery">
row="1"
class="emptyState"
v-if="!filteredRecipes.length && searchQuery"
>
<Label class="bx icon" :text="icon.search" textWrap="true" /> <Label class="bx icon" :text="icon.search" textWrap="true" />
<Label class="title orkm" text="No recipes found" textWrap="true" /> <Label class="title orkm" :text="'No recipes found' | L" textWrap="true" />
<Label <Label :text="`${noResultFor}` | L" textWrap="true" />
:text="
`Your search &quot;${searchQuery}&quot; did not match any recipes${noResultFor}`
"
textWrap="true"
/>
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
<GridLayout id="btnFabContainer" rows="*, auto" columns="*, auto"> <GridLayout id="btnFabContainer" rows="*, auto" columns="*, auto">
<transition name="bounce"> <transition name="bounce">
<MDFloatingActionButton <MDFloatingActionButton v-if="showFAB" row="1" col="1" class="bx fab-button" src="res://plus" @tap="addRecipe" />
v-if="showFAB"
row="1"
col="1"
class="bx fab-button"
src="res://plus"
@tap="addRecipe"
/>
</transition> </transition>
</GridLayout> </GridLayout>
</AbsoluteLayout> </AbsoluteLayout>
@ -218,29 +93,31 @@
</template> </template>
<script> <script>
import { AndroidApplication, Utils } from "@nativescript/core" import {
import { mapActions, mapState } from "vuex" AndroidApplication,
Utils,
import EditRecipe from "./EditRecipe.vue" Observable
import ViewRecipe from "./ViewRecipe.vue" }
from "@nativescript/core";
import ActionDialog from "./modal/ActionDialog.vue" import {
import ConfirmDialog from "./modal/ConfirmDialog.vue" localize
}
import * as utils from "~/shared/utils" from "@nativescript/localize"
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";
export default { export default {
props: [ props: [ "filterFavourites", "filterTrylater", "closeDrawer", "selectedCategory", "hijackGlobalBackEvent", "releaseGlobalBackEvent" ],
"filterFavorites",
"filterTrylater",
"closeDrawer",
"selectedCategory",
"hijackGlobalBackEvent",
"releaseGlobalBackEvent",
],
components: { components: {
EditRecipe, EditRecipe,
ViewRecipe, ViewRecipe
}, },
data() { data() {
return { return {
@ -250,245 +127,228 @@ export default {
rightAction: false, rightAction: false,
sortType: "Natural order", sortType: "Natural order",
deletionDialogActive: false, deletionDialogActive: false,
showFAB: false, showFAB: false
} };
}, },
computed: { computed: {
...mapState( [ "icon", "recipes", "currentComponent" ] ), ...mapState( [ "icon", "recipes", "currentComponent" ] ),
filteredRecipes() { filteredRecipes() {
if (this.filterFavorites) { if ( this.filterFavourites ) {
return this.recipes.filter( return this.recipes.filter( e => e.isFavorite && e.title.toLowerCase().includes( this.searchQuery ) );
(e) => }
e.isFavorite && e.title.toLowerCase().includes(this.searchQuery) else if ( this.filterTrylater ) {
) return this.recipes.filter( e => !e.tried && e.title.toLowerCase().includes( this.searchQuery ) );
} else if (this.filterTrylater) { }
return this.recipes.filter( else if ( this.selectedCategory ) {
(e) => !e.tried && e.title.toLowerCase().includes(this.searchQuery) return this.recipes.filter( e => e.category === this.selectedCategory && e.title.toLowerCase().includes( this.searchQuery ) );
) }
} else if (this.selectedCategory) { else {
return this.recipes.filter( return this.recipes.filter( e => e.title.toLowerCase().includes( this.searchQuery ) );
(e) =>
e.category === this.selectedCategory &&
e.title.toLowerCase().includes(this.searchQuery)
)
} else {
return this.recipes.filter((e) =>
e.title.toLowerCase().includes(this.searchQuery)
)
} }
}, },
noResultFor() { noResultFor() {
if (this.selectedCategory) return " in this category" if ( this.selectedCategory ) return "Your search did not match any recipes in this category";
if (this.filterFavorites) return " in your favorites" if ( this.filterFavourites ) return "Your search did not match any recipes in your favourites";
if (this.filterTrylater) return " in your try later list" if ( this.filterTrylater ) return "Your search did not match any recipes in your try later list";
return "" return "Your search did not match any recipes";
}, }
}, },
methods: { methods: {
...mapActions( [ "setCurrentComponentAction", "deleteRecipeAction" ] ), ...mapActions( [ "setCurrentComponentAction", "deleteRecipeAction" ] ),
onPageLoad() { onPageLoad( args ) {
this.filterFavorites const page = args.object;
? this.setComponent("Favorites") page.bindingContext = new Observable();
: this.filterTrylater this.filterFavourites ? this.setComponent( "Favourites" ) : this.filterTrylater ? this.setComponent( "Try Later" ) : this.selectedCategory ? this.setComponent( this.selectedCategory ) : this.setComponent( "EnRecipes" );
? this.setComponent("Try Later") this.showFAB = true;
: this.selectedCategory },
? this.setComponent(this.selectedCategory) onPageUnload() {
: this.setComponent("EnRecipes") this.releaseGlobalBackEvent();
this.showFAB = true
}, },
// HELPERS // HELPERS
showDrawer() { showDrawer() {
utils.showDrawer() utils.showDrawer();
}, },
openSearch() { openSearch() {
this.showSearch = true this.showSearch = true;
this.showFAB = false this.showFAB = false;
this.hijackLocalBackEvent() this.hijackLocalBackEvent();
}, },
closeSearch() { closeSearch() {
if (this.searchQuery) this.updateFilter() if ( this.searchQuery ) this.updateFilter();
this.searchQuery = "" this.searchQuery = "";
Utils.ad.dismissSoftInput() Utils.ad.dismissSoftInput();
this.showSearch = false this.showSearch = false;
this.showFAB = true this.showFAB = true;
this.releaseLocalBackEvent() this.releaseLocalBackEvent();
}, },
setComponent( comp ) { setComponent( comp ) {
this.setCurrentComponentAction(comp) this.setCurrentComponentAction( comp );
this.hijackGlobalBackEvent() this.hijackGlobalBackEvent();
}, },
clearSearch() { clearSearch() {
if ( this.searchQuery !== "" ) { if ( this.searchQuery !== "" ) {
this.updateFilter() this.updateFilter();
} }
}, },
formattedTotalTime( prepTime, cookTime ) { formattedTotalTime( prepTime, cookTime ) {
let t1 = prepTime.split(":") let t1 = prepTime.split( ":" );
let t2 = cookTime.split(":") let t2 = cookTime.split( ":" );
let h = parseInt(t1[0]) + parseInt(t2[0]) let h = parseInt( t1[ 0 ] ) + parseInt( t2[ 0 ] );
let m = parseInt(t1[1]) + parseInt(t2[1]) let m = parseInt( t1[ 1 ] ) + parseInt( t2[ 1 ] );
let hr = localize( 'hr' )
let min = localize( 'min' )
return { return {
time: h ? (m ? `${h} hr ${m} min` : `${h} hr`) : `${m} min`, time: h ? ( m ? `${h} ${hr} ${m} ${min}` : `${h} ${hr}` ) : `${m} ${min}`,
duration: `${h}${m}`, duration: `${h}${m}`
} };
}, },
onScroll( args ) { onScroll( args ) {
this.viewIsScrolled = args.scrollOffset ? true : false this.viewIsScrolled = args.scrollOffset ? true : false;
}, },
// NAVIGATION HANDLERS // NAVIGATION HANDLERS
hijackLocalBackEvent() { hijackLocalBackEvent() {
this.releaseGlobalBackEvent() this.releaseGlobalBackEvent();
AndroidApplication.on( AndroidApplication.on( AndroidApplication.activityBackPressedEvent, this.searchBackEvent );
AndroidApplication.activityBackPressedEvent,
this.searchBackEvent
)
}, },
releaseLocalBackEvent() { releaseLocalBackEvent() {
AndroidApplication.off( AndroidApplication.off( AndroidApplication.activityBackPressedEvent, this.searchBackEvent );
AndroidApplication.activityBackPressedEvent, this.hijackGlobalBackEvent();
this.searchBackEvent
)
this.hijackGlobalBackEvent()
}, },
searchBackEvent( args ) { searchBackEvent( args ) {
args.cancel = true args.cancel = true;
this.closeDrawer() this.closeDrawer();
this.closeSearch() this.closeSearch();
}, },
addRecipe() { addRecipe() {
this.showFAB = false this.showFAB = false;
this.releaseGlobalBackEvent() this.releaseGlobalBackEvent();
this.$navigateTo( EditRecipe, { this.$navigateTo( EditRecipe, {
props: { props: {
selectedCategory: this.selectedCategory, selectedCategory: this.selectedCategory,
filterFavorites: this.filterFavorites, filterFavourites: this.filterFavourites
}, }
}) } );
}, },
viewRecipe( recipeID ) { viewRecipe( recipeID ) {
this.showFAB = false this.showFAB = false;
this.$navigateTo( ViewRecipe, { this.$navigateTo( ViewRecipe, {
props: { props: {
filterTrylater: this.filterTrylater, filterTrylater: this.filterTrylater,
recipeID, recipeID
}, },
backstackVisible: false, backstackVisible: false
}) } );
}, },
// LIST HANDLERS // LIST HANDLERS
sortDialog() { sortDialog() {
this.releaseGlobalBackEvent() this.releaseGlobalBackEvent();
this.$showModal( ActionDialog, { this.$showModal( ActionDialog, {
props: { props: {
title: "Sort by", title: "Sort by",
list: [ "Natural order", "Title", "Duration", "Last modified" ], list: [ "Natural order", "Title", "Duration", "Last modified" ],
height: "192", height: "192"
},
}).then((action) => {
if (action && action !== "Cancel" && this.sortType !== action) {
this.sortType = action
this.updateSort()
} }
this.hijackGlobalBackEvent() } ).then( action => {
}) if ( action && action !== "Cancel" && this.sortType !== action ) {
this.sortType = action;
this.updateSort();
}
this.hijackGlobalBackEvent();
} );
}, },
updateSort() { updateSort() {
let listView = this.$refs.listView.nativeView let listView = this.$refs.listView.nativeView;
listView.sortingFunction = undefined listView.sortingFunction = undefined;
listView.sortingFunction = this.sortFunction listView.sortingFunction = this.sortFunction;
}, },
sortFunction( item, otherItem ) { sortFunction( item, otherItem ) {
const titleOrder = item.title const titleOrder = item.title.toLowerCase().localeCompare( otherItem.title.toLowerCase(), "en", {
.toLowerCase() ignorePunctuation: true
.localeCompare(otherItem.title.toLowerCase(), "en", { } );
ignorePunctuation: true, let d1 = this.formattedTotalTime( item.prepTime, item.cookTime ).duration;
}) let d2 = this.formattedTotalTime( otherItem.prepTime, otherItem.cookTime ).duration;
let d1 = this.formattedTotalTime(item.prepTime, item.cookTime).duration let ld1 = new Date( item.lastModified );
let d2 = this.formattedTotalTime(otherItem.prepTime, otherItem.cookTime) let ld2 = new Date( otherItem.lastModified );
.duration
let ld1 = new Date(item.lastModified)
let ld2 = new Date(otherItem.lastModified)
switch ( this.sortType ) { switch ( this.sortType ) {
case "Title": case "Title":
return titleOrder > 0 ? -1 : titleOrder < 0 ? 1 : 0 return titleOrder > 0 ? -1 : titleOrder < 0 ? 1 : 0;
break break;
case "Duration": case "Duration":
return d1 > d2 ? -1 : d1 < d2 ? 1 : 0 return d1 > d2 ? -1 : d1 < d2 ? 1 : 0;
break break;
case "Last modified": case "Last modified":
return ld1 < ld2 ? -1 : ld1 > ld2 ? 1 : 0 return ld1 < ld2 ? -1 : ld1 > ld2 ? 1 : 0;
break break;
default: default:
return 0 return 0;
break break;
} }
}, },
updateFilter() { updateFilter() {
let listView = this.$refs.listView.nativeView let listView = this.$refs.listView.nativeView;
setTimeout((e) => { setTimeout( e => {
listView.filteringFunction = undefined listView.filteringFunction = undefined;
listView.filteringFunction = this.filterFunction listView.filteringFunction = this.filterFunction;
}, 1) }, 1 );
}, },
filterFunction( item ) { filterFunction( item ) {
if (this.filterFavorites) { if ( this.filterFavourites ) {
return item.isFavorite return item.isFavorite ? item.title.toLowerCase().includes( this.searchQuery ) : false;
? item.title.toLowerCase().includes(this.searchQuery) }
: false else if ( this.filterTrylater ) {
} else if (this.filterTrylater) { return item.tried ? false : item.title.toLowerCase().includes( this.searchQuery );
return item.tried }
? false else if ( this.selectedCategory ) {
: item.title.toLowerCase().includes(this.searchQuery) return item.category === this.selectedCategory ? item.title.toLowerCase().includes( this.searchQuery ) : false;
} else if (this.selectedCategory) { }
return item.category === this.selectedCategory else {
? item.title.toLowerCase().includes(this.searchQuery) return item.title.toLowerCase().includes( this.searchQuery );
: false
} else {
return item.title.toLowerCase().includes(this.searchQuery)
} }
}, },
onSwiping( {
onSwiping({ data, object }) { data,
const swipeLimits = data.swipeLimits object
const swipeView = object } ) {
const rightItem = swipeView.getViewById("delete-action") const swipeLimits = data.swipeLimits;
swipeLimits.right = rightItem.getMeasuredWidth() - 12 const swipeView = object;
swipeLimits.threshold = swipeLimits.right - 6 const rightItem = swipeView.getViewById( "delete-action" );
swipeLimits.right = rightItem.getMeasuredWidth() - 12;
swipeLimits.threshold = swipeLimits.right - 6;
if ( data.x < -swipeLimits.threshold ) { if ( data.x < -swipeLimits.threshold ) {
this.rightAction = true this.rightAction = true;
swipeView.notifySwipeToExecuteFinished() swipeView.notifySwipeToExecuteFinished();
} }
}, },
onSwipeEnded({ index }) { onSwipeEnded( {
let recipeID = this.recipes[index].id index
if (this.rightAction && !this.deletionDialogActive) } ) {
this.deleteRecipe(index, recipeID) let recipeID = this.recipes[ index ].id;
this.rightAction = false if ( this.rightAction && !this.deletionDialogActive ) this.deleteRecipe( index, recipeID );
this.rightAction = false;
}, },
// DATA HANDLERS // DATA HANDLERS
deleteRecipe( index, recipeID ) { deleteRecipe( index, recipeID ) {
this.deletionDialogActive = true this.deletionDialogActive = true;
this.$showModal( ConfirmDialog, { this.$showModal( ConfirmDialog, {
props: { props: {
title: "Delete recipe", title: localize( "Delete recipe?" ),
description: `Are you sure you want to delete the recipe "${this.recipes[index].title}"?`, description: `${localize('Are you sure you want to delete the recipe')} "${this.recipes[index].title}"?`,
cancelButtonText: "CANCEL", cancelButtonText: "CANCEL",
okButtonText: "DELETE", okButtonText: "DELETE"
}, }
}).then((action) => { } ).then( action => {
if (action) { if ( action ) {
this.deleteRecipeAction({ index, id: recipeID }) this.deleteRecipeAction( {
index,
id: recipeID
} );
}
this.deletionDialogActive = false;
} );
} }
this.deletionDialogActive = false
})
},
}, },
mounted() { mounted() {
this.showFAB = true this.showFAB = true;
},
} }
};
</script> </script>

View file

@ -2,79 +2,25 @@
<Page @loaded="onPageLoad"> <Page @loaded="onPageLoad">
<ActionBar flat="true"> <ActionBar flat="true">
<GridLayout rows="*" columns="auto, *, auto"> <GridLayout rows="*" columns="auto, *, auto">
<MDButton <MDButton class="bx left" variant="text" :text="icon.menu" automationText="Back" @tap="showDrawer" col="0" />
class="bx left" <Label class="title orkm" :text="'Meal Planner' | L" col="1" />
variant="text" <MDButton class="bx left" variant="text" :text="icon.today" automationText="today" @tap="goToToday" col="2" />
:text="icon.menu"
automationText="Back"
@tap="showDrawer"
col="0"
/>
<Label class="title orkm" text="Meal Planner" col="1" />
<MDButton
class="bx left"
variant="text"
:text="icon.today"
automationText="today"
@tap="goToToday"
col="2"
/>
</GridLayout> </GridLayout>
</ActionBar> </ActionBar>
<GridLayout rows="280, *"> <GridLayout rows="280, *">
<RadCalendar <RadCalendar :androidElevation="viewIsScrolled ? 4 : 0" class="orkm" row="0" ref="calendar" @loaded="onCalendarLoad" @dateSelected="onDateSelected" :viewMode="viewMode" :transitionMode="transitionMode" :selectionMode="selectionMode"
:androidElevation="viewIsScrolled ? 4 : 0" :eventsViewMode="eventsViewMode" :eventSource="getMealPlans"></RadCalendar>
class="orkm"
row="0"
ref="calendar"
locale="en-US"
@loaded="onCalendarLoad"
@dateSelected="onDateSelected"
:viewMode="viewMode"
:transitionMode="transitionMode"
:selectionMode="selectionMode"
:eventsViewMode="eventsViewMode"
:eventSource="getMealPlans"
></RadCalendar>
<ScrollView row="1" width="100%" height="100%" @scroll="onScroll"> <ScrollView row="1" width="100%" height="100%" @scroll="onScroll">
<StackLayout class="dayPlan"> <StackLayout class="dayPlan">
<StackLayout <StackLayout v-for="(mealType, index) in mealTimes" :key="'mealType' + index" class="plansContainer" :class="mealType">
v-for="(mealType, index) in mealTimes"
:key="'mealType' + index"
class="plansContainer"
:class="mealType"
>
<GridLayout columns="*, auto" class="header"> <GridLayout columns="*, auto" class="header">
<Label col="0" class="periodLabel orkm" :text="mealType" /> <Label col="0" class="periodLabel orkm" :text="mealType | L" />
<MDButton <MDButton col="1" variant="text" class="bx addMeal" :text="icon.plus" @tap="addRecipe(mealType)" />
col="1"
variant="text"
class="bx addMeal"
:text="icon.plus"
@tap="addRecipe(mealType)"
/>
</GridLayout> </GridLayout>
<GridLayout <GridLayout class="recipes" columns="*, auto" v-for="(recipeID, index) in getRecipes[mealType]" :key="mealType + index">
class="recipes"
columns="*, auto"
v-for="(recipeID, index) in getRecipes[mealType]"
:key="mealType + index"
>
<MDRipple @tap="viewRecipe(recipeID)" /> <MDRipple @tap="viewRecipe(recipeID)" />
<Label <Label verticalAlignment="center" class="recipeTitle" col="0" :text="getRecipeTitle(recipeID)" textWrap="true" />
verticalAlignment="center" <MDButton variant="text" col="1" class="bx closeBtn" :text="icon.close" @tap="removeRecipe(mealType, recipeID)" />
class="recipeTitle"
col="0"
:text="getRecipeTitle(recipeID)"
textWrap="true"
/>
<MDButton
variant="text"
col="1"
class="bx closeBtn"
:text="icon.close"
@tap="removeRecipe(mealType, recipeID)"
/>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
@ -84,7 +30,14 @@
</template> </template>
<script> <script>
import { ApplicationSettings, Color, Page } from "@nativescript/core" import {
ApplicationSettings,
Color,
Page,
Observable,
Device
}
from "@nativescript/core"
import { import {
CalendarViewMode, CalendarViewMode,
CalendarTransitionMode, CalendarTransitionMode,
@ -96,9 +49,14 @@ import {
CalendarCellAlignment, CalendarCellAlignment,
CellStyle, CellStyle,
CalendarEventsViewMode, CalendarEventsViewMode,
CalendarEvent, CalendarEvent
} from "nativescript-ui-calendar" }
import { mapState, mapActions } from "vuex" from "nativescript-ui-calendar"
import {
mapState,
mapActions
}
from "vuex"
import ViewRecipe from "./ViewRecipe.vue" import ViewRecipe from "./ViewRecipe.vue"
@ -149,39 +107,39 @@ export default {
}, },
monthViewStyle() { monthViewStyle() {
const monthViewStyle = new CalendarMonthViewStyle() const monthViewStyle = new CalendarMonthViewStyle()
monthViewStyle.backgroundColor = this.isLightMode monthViewStyle.backgroundColor = this.isLightMode ?
? this.color.gray1 this.color.gray1 :
: this.color.gray9 this.color.gray9
monthViewStyle.showTitle = true monthViewStyle.showTitle = true
monthViewStyle.showWeekNumbers = false monthViewStyle.showWeekNumbers = false
monthViewStyle.showDayNames = true monthViewStyle.showDayNames = true
const titleCellStyle = new DayCellStyle() const titleCellStyle = new DayCellStyle()
titleCellStyle.cellBackgroundColor = this.isLightMode titleCellStyle.cellBackgroundColor = this.isLightMode ?
? this.color.gray2 this.color.gray2 :
: this.color.black this.color.black
titleCellStyle.cellBorderWidth = 1 titleCellStyle.cellBorderWidth = 1
titleCellStyle.cellBorderColor = this.isLightMode titleCellStyle.cellBorderColor = this.isLightMode ?
? this.color.gray2 this.color.gray2 :
: this.color.black this.color.black
titleCellStyle.cellTextSize = 16 titleCellStyle.cellTextSize = 16
titleCellStyle.cellTextColor = this.isLightMode titleCellStyle.cellTextColor = this.isLightMode ?
? this.color.gray9 this.color.gray9 :
: this.color.gray1 this.color.gray1
titleCellStyle.cellTextFontName = this.appFontMedium titleCellStyle.cellTextFontName = this.appFontMedium
monthViewStyle.titleCellStyle = titleCellStyle monthViewStyle.titleCellStyle = titleCellStyle
const dayNameCellStyle = new CellStyle() const dayNameCellStyle = new CellStyle()
dayNameCellStyle.cellBackgroundColor = this.isLightMode dayNameCellStyle.cellBackgroundColor = this.isLightMode ?
? this.color.gray2 this.color.gray2 :
: this.color.black this.color.black
dayNameCellStyle.cellTextColor = this.isLightMode dayNameCellStyle.cellTextColor = this.isLightMode ?
? this.color.gray9 this.color.gray9 :
: this.color.gray1 this.color.gray1
dayNameCellStyle.cellBorderWidth = 1 dayNameCellStyle.cellBorderWidth = 1
dayNameCellStyle.cellBorderColor = this.isLightMode dayNameCellStyle.cellBorderColor = this.isLightMode ?
? this.color.gray2 this.color.gray2 :
: this.color.black this.color.black
dayNameCellStyle.cellTextSize = 10 dayNameCellStyle.cellTextSize = 10
dayNameCellStyle.cellAlignment = CalendarCellAlignment.Center dayNameCellStyle.cellAlignment = CalendarCellAlignment.Center
dayNameCellStyle.cellTextFontName = this.appFontMedium dayNameCellStyle.cellTextFontName = this.appFontMedium
@ -194,46 +152,46 @@ export default {
dayCellStyle.eventFontStyle = CalendarFontStyle.Bold dayCellStyle.eventFontStyle = CalendarFontStyle.Bold
dayCellStyle.eventTextSize = 8 dayCellStyle.eventTextSize = 8
dayCellStyle.cellTextSize = 16 dayCellStyle.cellTextSize = 16
dayCellStyle.cellTextColor = this.isLightMode dayCellStyle.cellTextColor = this.isLightMode ?
? this.color.gray9 this.color.gray9 :
: this.color.gray1 this.color.gray1
dayCellStyle.cellAlignment = CalendarCellAlignment.Bottom dayCellStyle.cellAlignment = CalendarCellAlignment.Bottom
dayCellStyle.cellBackgroundColor = this.isLightMode dayCellStyle.cellBackgroundColor = this.isLightMode ?
? this.color.gray1 this.color.gray1 :
: this.color.gray9 this.color.gray9
dayCellStyle.cellTextFontName = this.appFontRegular dayCellStyle.cellTextFontName = this.appFontRegular
dayCellStyle.cellBorderWidth = 1 dayCellStyle.cellBorderWidth = 1
dayCellStyle.cellBorderColor = this.isLightMode dayCellStyle.cellBorderColor = this.isLightMode ?
? this.color.gray2 this.color.gray2 :
: this.color.black this.color.black
monthViewStyle.dayCellStyle = dayCellStyle monthViewStyle.dayCellStyle = dayCellStyle
const todayCellStyle = new DayCellStyle() const todayCellStyle = new DayCellStyle()
todayCellStyle.cellBackgroundColor = this.isLightMode todayCellStyle.cellBackgroundColor = this.isLightMode ?
? this.color.gray1 this.color.gray1 :
: this.color.gray9 this.color.gray9
todayCellStyle.cellTextColor = this.color.orange todayCellStyle.cellTextColor = this.color.orange
todayCellStyle.cellBorderWidth = 1 todayCellStyle.cellBorderWidth = 1
todayCellStyle.cellTextFontName = this.appFontMedium todayCellStyle.cellTextFontName = this.appFontMedium
todayCellStyle.cellTextFontStyle = CalendarFontStyle.Bold todayCellStyle.cellTextFontStyle = CalendarFontStyle.Bold
todayCellStyle.cellTextSize = 16 todayCellStyle.cellTextSize = 16
todayCellStyle.cellAlignment = CalendarCellAlignment.Bottom todayCellStyle.cellAlignment = CalendarCellAlignment.Bottom
todayCellStyle.cellBorderColor = this.isLightMode todayCellStyle.cellBorderColor = this.isLightMode ?
? this.color.gray2 this.color.gray2 :
: this.color.black this.color.black
monthViewStyle.todayCellStyle = todayCellStyle monthViewStyle.todayCellStyle = todayCellStyle
const selectedCellStyle = new DayCellStyle() const selectedCellStyle = new DayCellStyle()
selectedCellStyle.eventTextSize = 1 selectedCellStyle.eventTextSize = 1
selectedCellStyle.cellAlignment = CalendarCellAlignment.Bottom selectedCellStyle.cellAlignment = CalendarCellAlignment.Bottom
selectedCellStyle.cellBackgroundColor = this.isLightMode selectedCellStyle.cellBackgroundColor = this.isLightMode ?
? this.color.white this.color.white :
: this.color.gray8 this.color.gray8
selectedCellStyle.cellBorderWidth = 1 selectedCellStyle.cellBorderWidth = 1
selectedCellStyle.cellBorderColor = this.color.orange selectedCellStyle.cellBorderColor = this.color.orange
selectedCellStyle.cellTextColor = this.isLightMode selectedCellStyle.cellTextColor = this.isLightMode ?
? this.color.gray9 this.color.gray9 :
: this.color.gray1 this.color.gray1
selectedCellStyle.cellTextFontName = this.appFontMedium selectedCellStyle.cellTextFontName = this.appFontMedium
selectedCellStyle.cellTextFontStyle = CalendarFontStyle.Bold selectedCellStyle.cellTextFontStyle = CalendarFontStyle.Bold
selectedCellStyle.cellTextSize = 16 selectedCellStyle.cellTextSize = 16
@ -262,7 +220,8 @@ export default {
} }
return acc return acc
}, {} ) }, {} )
} else return 0 }
else return 0
}, },
getMealPlans() { getMealPlans() {
const getDate = ( date ) => { const getDate = ( date ) => {
@ -297,9 +256,12 @@ export default {
"deleteMealPlanAction", "deleteMealPlanAction",
] ), ] ),
onPageLoad( args ) { onPageLoad( args ) {
const page = args.object;
page.bindingContext = new Observable();
this.setCurrentComponentAction( "MealPlanner" ) this.setCurrentComponentAction( "MealPlanner" )
}, },
onCalendarLoad( args ) { onCalendarLoad( args ) {
args.object.locale = `${Device.language}-${Device.language.toUpperCase()}`
args.object.monthViewStyle = this.monthViewStyle args.object.monthViewStyle = this.monthViewStyle
args.object.android args.object.android
.getGestureManager() .getGestureManager()
@ -358,9 +320,9 @@ export default {
// DATA HANDLERS // DATA HANDLERS
addRecipe( mealType ) { addRecipe( mealType ) {
let filteredRecipes = this.recipes.filter( ( e ) => let filteredRecipes = this.recipes.filter( ( e ) =>
this.getRecipes[mealType] this.getRecipes[ mealType ] ?
? !this.getRecipes[mealType].includes(e.id) !this.getRecipes[ mealType ].includes( e.id ) :
: true true
) )
this.$showModal( ActionDialogWithSearch, { this.$showModal( ActionDialogWithSearch, {
props: { props: {
@ -426,7 +388,11 @@ export default {
d: date.getDate(), d: date.getDate(),
} }
} }
let { y, m, d } = selectedDate() let {
y,
m,
d
} = selectedDate()
let mealTime = { let mealTime = {
breakfast: { breakfast: {
start: new Date( y, m, d, 0 ), start: new Date( y, m, d, 0 ),
@ -452,7 +418,10 @@ export default {
false, false,
new Color( this.color[ mealType ] ) new Color( this.color[ mealType ] )
) )
this.addMealPlanAction({ event, eventColor: this.color[mealType] }) this.addMealPlanAction( {
event,
eventColor: this.color[ mealType ]
} )
this.updateSelectedDatePlans() this.updateSelectedDatePlans()
}, },
goToToday() { goToToday() {

View file

@ -2,67 +2,49 @@
<Page @loaded="onPageLoad"> <Page @loaded="onPageLoad">
<ActionBar :androidElevation="viewIsScrolled ? 4 : 0"> <ActionBar :androidElevation="viewIsScrolled ? 4 : 0">
<GridLayout rows="*" columns="auto, *"> <GridLayout rows="*" columns="auto, *">
<MDButton <MDButton class="bx left" variant="text" :text="icon.menu" automationText="Back" @tap="showDrawer" col="0" />
class="bx left" <Label class="title orkm" :text="'Settings' | L" col="1" />
variant="text"
:text="icon.menu"
automationText="Back"
@tap="showDrawer"
col="0"
/>
<Label class="title orkm" text="Settings" col="1" />
</GridLayout> </GridLayout>
</ActionBar> </ActionBar>
<ScrollView @scroll="onScroll"> <ScrollView @scroll="onScroll">
<StackLayout class="main-container"> <StackLayout class="main-container">
<Label text="Interface" class="group-header orkm" /> <Label :text="'Interface' | L" class="group-header orkm" />
<GridLayout columns="auto, *" class="option">
<MDRipple colSpan="2" @tap="selectAppLanguage" />
<Label col="0" verticalAlignment="center" class="bx" :text="icon.globe" />
<StackLayout col="1">
<Label :text="'App language' | L" />
<Label :text="appLanguage" class="info" />
</StackLayout>
</GridLayout>
<GridLayout columns="auto, *" class="option"> <GridLayout columns="auto, *" class="option">
<MDRipple colSpan="2" @tap="selectThemes" /> <MDRipple colSpan="2" @tap="selectThemes" />
<Label <Label col="0" verticalAlignment="center" class="bx" :text="icon.theme" />
col="0"
verticalAlignment="center"
class="bx"
:text="icon.theme"
/>
<StackLayout col="1"> <StackLayout col="1">
<Label text="Theme" /> <Label :text="'Theme' | L" />
<Label :text="appTheme" class="info" /> <Label :text="`${appTheme}` | L" class="info" />
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
<StackLayout class="hr m-10"></StackLayout> <StackLayout class="hr m-10"></StackLayout>
<Label text="Database" class="group-header orkm" /> <Label :text="'Database' | L" class="group-header orkm" />
<GridLayout columns="auto, *" class="option"> <GridLayout columns="auto, *" class="option">
<!-- <MDRipple colSpan="2" @tap="folderPicker" /> -->
<MDRipple colSpan="2" @tap="exportCheck" /> <MDRipple colSpan="2" @tap="exportCheck" />
<Label col="0" class="bx" :text="icon.export" /> <Label col="0" class="bx" :text="icon.export" />
<StackLayout col="1"> <StackLayout col="1">
<Label text="Export a full backup" /> <Label :text="'Export a full backup' | L" textWrap="true" />
<Label <Label v-if="!backupInProgress" :text="'Generates a zip file that contains all your data. This file can be imported back.' | L" class="info" textWrap="true" />
v-if="!backupInProgress"
text="Generates a zip file that contains all your data. This file can be imported back."
class="info"
textWrap="true"
/>
<GridLayout class="progressContainer" v-else columns="*, 64"> <GridLayout class="progressContainer" v-else columns="*, 64">
<MDProgress <MDProgress col="0" :value="backupProgress" maxValue="100"></MDProgress>
col="0"
:value="backupProgress"
maxValue="100"
></MDProgress>
<Label col="1" :text="` ${backupProgress}%`" /> <Label col="1" :text="` ${backupProgress}%`" />
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
<GridLayout columns="auto, *" class="option" <GridLayout columns="auto, *" class="option">
><MDRipple colSpan="2" @tap="importCheck" /> <MDRipple colSpan="2" @tap="importCheck" />
<Label col="0" class="bx" :text="icon.import" /> <Label col="0" class="bx" :text="icon.import" />
<StackLayout col="1"> <StackLayout col="1">
<Label text="Import from backup" /> <Label :text="'Import from backup' | L" textWrap="true" />
<Label <Label :text="'Supports full backups exported by this app' | L" class="info" textWrap="true" />
text="Supports full backups exported by this app"
class="info"
textWrap="true"
/>
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
@ -77,13 +59,28 @@ import {
knownFolders, knownFolders,
File, File,
Folder, Folder,
} from "@nativescript/core" Observable,
Device
}
from "@nativescript/core"
import * as Permissions from "@nativescript-community/perms" import * as Permissions from "@nativescript-community/perms"
import { Zip } from "@nativescript/zip" import {
Zip
}
from "@nativescript/zip"
import * as Toast from "nativescript-toast" import * as Toast from "nativescript-toast"
import * as Filepicker from "nativescript-plugin-filepicker" import * as Filepicker from "nativescript-plugin-filepicker"
import Theme from "@nativescript/theme" import Theme from "@nativescript/theme"
import { mapState, mapActions } from "vuex" import {
localize,
overrideLocale
}
from "@nativescript/localize"
import {
mapState,
mapActions
}
from "vuex"
import ActionDialog from "./modal/ActionDialog.vue" import ActionDialog from "./modal/ActionDialog.vue"
import ConfirmDialog from "./modal/ConfirmDialog.vue" import ConfirmDialog from "./modal/ConfirmDialog.vue"
@ -95,6 +92,7 @@ export default {
return { return {
viewIsScrolled: false, viewIsScrolled: false,
appTheme: "Light", appTheme: "Light",
appLanguage: "English",
backupProgress: 0, backupProgress: 0,
backupInProgress: false, backupInProgress: false,
} }
@ -107,6 +105,7 @@ export default {
"userYieldUnits", "userYieldUnits",
"mealPlans", "mealPlans",
"currentComponent", "currentComponent",
"language",
] ), ] ),
}, },
methods: { methods: {
@ -117,7 +116,9 @@ export default {
"importRecipesAction", "importRecipesAction",
"importMealPlansAction", "importMealPlansAction",
] ), ] ),
onPageLoad() { onPageLoad( args ) {
const page = args.object;
page.bindingContext = new Observable();
this.setCurrentComponentAction( "Settings" ) this.setCurrentComponentAction( "Settings" )
}, },
// HELPERS // HELPERS
@ -128,6 +129,39 @@ export default {
this.viewIsScrolled = args.scrollY ? true : false this.viewIsScrolled = args.scrollY ? true : false
}, },
// LANGUAGE SELECTION
selectAppLanguage() {
let languages = this.language.map( e => e.title )
this.$showModal( ActionDialog, {
props: {
title: "App language",
list: [ ...languages ],
},
} ).then( ( action ) => {
if ( action && action !== "Cancel" && this.appLanguage !== action ) {
let currentLocale = Device.language.split( '-' )[ 0 ]
let locale = this.language.filter( e => e.title === action )[ 0 ].locale
if ( currentLocale !== locale ) {
this.$showModal( ConfirmDialog, {
props: {
title: "Restart required",
description: localize( "EnRecipes needs to be restarted for the app language to take effect." ),
cancelButtonText: "CANCEL",
okButtonText: "RESTART",
},
} ).then( ( result ) => {
if ( result ) {
this.appLanguage = action
ApplicationSettings.setString( "appLanguage", action )
overrideLocale( locale )
setTimeout( ( e ) => utils.restartApp(), 250 )
}
} )
}
}
} )
},
// THEME SELECTION // THEME SELECTION
selectThemes() { selectThemes() {
this.$showModal( ActionDialog, { this.$showModal( ActionDialog, {
@ -140,11 +174,10 @@ export default {
if ( action && action !== "Cancel" && this.appTheme !== action ) { if ( action && action !== "Cancel" && this.appTheme !== action ) {
this.$showModal( ConfirmDialog, { this.$showModal( ConfirmDialog, {
props: { props: {
title: "Reload required", title: "Restart required",
description: description: localize( "EnRecipes needs to be restarted for the theme change to take effect." ),
"EnRecipes needs to be reloaded for the theme change to take effect.",
cancelButtonText: "CANCEL", cancelButtonText: "CANCEL",
okButtonText: "RELOAD", okButtonText: "RESTART",
}, },
} ).then( ( result ) => { } ).then( ( result ) => {
if ( result ) { if ( result ) {
@ -161,13 +194,14 @@ export default {
exportCheck() { exportCheck() {
if ( !this.recipes.length ) { if ( !this.recipes.length ) {
Toast.makeText( Toast.makeText(
"Add at least one recipe to perform a backup", localize( "Add at least one recipe to perform a backup" ),
"long" "long"
).show() ).show()
} else { }
else {
this.permissionCheck( this.permissionCheck(
this.permissionConfirmation, this.permissionConfirmation,
"EnRecipes requires storage permission in order to backup your data to this device.", localize( "EnRecipes requires storage permission in order to backup your data to this device." ),
this.exportBackup this.exportBackup
) )
} }
@ -253,7 +287,7 @@ export default {
importCheck() { importCheck() {
this.permissionCheck( this.permissionCheck(
this.permissionConfirmation, this.permissionConfirmation,
"EnRecipes requires storage permission in order to import your data from a previous backup.", localize( "EnRecipes requires storage permission in order to import your data from a previous backup." ),
this.openFilePicker this.openFilePicker
) )
}, },
@ -264,7 +298,7 @@ export default {
} ) } )
.present() .present()
.then( ( selection ) => { .then( ( selection ) => {
Toast.makeText("Processing...").show() Toast.makeText( localize( "Verifying..." ) ).show()
let zipPath = selection[ 0 ] let zipPath = selection[ 0 ]
this.validateZipContent( zipPath ) this.validateZipContent( zipPath )
} ) } )
@ -311,17 +345,29 @@ export default {
const userYieldUnitsFilePath = cacheFolderPath + "/userYieldUnits.json" const userYieldUnitsFilePath = cacheFolderPath + "/userYieldUnits.json"
const mealPlansFilePath = cacheFolderPath + "/mealPlans.json" const mealPlansFilePath = cacheFolderPath + "/mealPlans.json"
if ( Folder.exists( cacheFolderPath ) ) { if ( Folder.exists( cacheFolderPath ) ) {
this.isFileDataValid([ this.isFileDataValid( [ {
{
zipPath, zipPath,
path: EnRecipesFilePath, path: EnRecipesFilePath,
db: "EnRecipesDB", db: "EnRecipesDB",
}, },
{ zipPath, path: userCategoriesFilePath, db: "userCategoriesDB" }, {
{ zipPath, path: userYieldUnitsFilePath, db: "userYieldUnitsDB" }, zipPath,
{ zipPath, path: mealPlansFilePath, db: "mealPlansDB" }, path: userCategoriesFilePath,
db: "userCategoriesDB"
},
{
zipPath,
path: userYieldUnitsFilePath,
db: "userYieldUnitsDB"
},
{
zipPath,
path: mealPlansFilePath,
db: "mealPlansDB"
},
] ) ] )
} else { }
else {
Folder.fromPath( extractedFolderPath ).remove() Folder.fromPath( extractedFolderPath ).remove()
Toast.makeText( Toast.makeText(
"Import failed. Backup file is incorrect or corrupt", "Import failed. Backup file is incorrect or corrupt",
@ -340,7 +386,7 @@ export default {
directory: dest, directory: dest,
overwrite: true, overwrite: true,
} ).then( ( res ) => { } ).then( ( res ) => {
Toast.makeText("Import successful", "long").show() Toast.makeText( localize( "Import successful" ), "long" ).show()
this.$navigateBack() this.$navigateBack()
} ) } )
}, },
@ -355,18 +401,20 @@ export default {
if ( status === "authorized" ) action() if ( status === "authorized" ) action()
if ( status !== "denied" ) if ( status !== "denied" )
ApplicationSettings.setBoolean( "storagePermissionAsked", true ) ApplicationSettings.setBoolean( "storagePermissionAsked", true )
else Toast.makeText("Permission denied").show() else Toast.makeText( localize( "Permission denied" ) ).show()
} ) } )
} }
} ) } )
} else { }
else {
Permissions.check( "photo" ).then( ( res ) => { Permissions.check( "photo" ).then( ( res ) => {
let status = res[ Object.keys( res )[ 0 ] ] let status = res[ Object.keys( res )[ 0 ] ]
if ( status !== "authorized" ) { if ( status !== "authorized" ) {
confirmation( description ).then( ( e ) => { confirmation( description ).then( ( e ) => {
e && utils.openAppSettingsPage() e && utils.openAppSettingsPage()
} ) } )
} else action() }
else action()
} ) } )
} }
}, },
@ -383,6 +431,7 @@ export default {
}, },
mounted() { mounted() {
this.appTheme = ApplicationSettings.getString( "appTheme", "Light" ) this.appTheme = ApplicationSettings.getString( "appTheme", "Light" )
this.appLanguage = ApplicationSettings.getString( "appLanguage", localize( "System default" ) )
}, },
} }
</script> </script>

View file

@ -2,216 +2,91 @@
<Page @loaded="onPageLoad" @unloaded="onPageUnload"> <Page @loaded="onPageLoad" @unloaded="onPageUnload">
<ActionBar flat="true"> <ActionBar flat="true">
<GridLayout rows="48" columns="auto, *, auto"> <GridLayout rows="48" columns="auto, *, auto">
<MDButton <MDButton variant="text" row="0" col="0" class="bx" :text="icon.back" automationText="Back" @tap="$navigateBack()" />
variant="text"
row="0"
col="0"
class="bx"
:text="icon.back"
automationText="Back"
@tap="$navigateBack()"
/>
<!-- <ScrollView
row="1"
col="0"
colSpan="3"
orientation="horizontal"
scrollBarIndicatorVisible="false"
>
<Label
class="title orkm"
:text="recipe.title"
verticalAlignment="bottom"
/>
</ScrollView> -->
<FlexboxLayout row="0" col="2" alignItems="center"> <FlexboxLayout row="0" col="2" alignItems="center">
<MDButton <MDButton v-if="!filterTrylater" variant="text" class="bx" :text="recipe.tried ? icon.trylaterOutline : icon.trylater" @tap="toggleTrylater" />
v-if="!filterTrylater" <MDButton v-else variant="text" class="bx" :text="icon.check" @tap="recipeTried" />
variant="text" <MDButton variant="text" class="bx" :text="recipe.isFavorite ? icon.heart : icon.heartOutline" @tap="toggleFavourite" />
class="bx" <MDButton variant="text" v-if="!busy" class="bx" :text="icon.edit" @tap="editRecipe" />
:text="recipe.tried ? icon.trylaterOutline : icon.trylater"
@tap="toggleTrylater"
/>
<MDButton
v-else
variant="text"
class="bx"
:text="icon.check"
@tap="recipeTried"
/>
<MDButton
variant="text"
class="bx"
:text="recipe.isFavorite ? icon.heart : icon.heartOutline"
@tap="toggleFavorite"
/>
<MDButton
variant="text"
v-if="!busy"
class="bx"
:text="icon.edit"
@tap="editRecipe"
/>
<MDActivityIndicator v-else :busy="busy" /> <MDActivityIndicator v-else :busy="busy" />
</FlexboxLayout> </FlexboxLayout>
</GridLayout> </GridLayout>
</ActionBar> </ActionBar>
<AbsoluteLayout> <AbsoluteLayout>
<Tabs <Tabs width="100%" height="100%" :selectedIndex="selectedTabIndex" @selectedIndexChange="selectedIndexChange" class="viewRecipe">
width="100%"
height="100%"
:selectedIndex="selectedTabIndex"
@selectedIndexChange="selectedIndexChange"
class="viewRecipe"
>
<TabStrip :androidElevation="viewIsScrolled ? 4 : 0"> <TabStrip :androidElevation="viewIsScrolled ? 4 : 0">
<TabStripItem> <TabStripItem>
<Label text="Overview"></Label> <Label :text="'Overview' | L"></Label>
</TabStripItem> </TabStripItem>
<TabStripItem> <TabStripItem>
<Label text="Ingredients"></Label> <Label :text="'Ingredients' | L"></Label>
</TabStripItem> </TabStripItem>
<TabStripItem> <TabStripItem>
<Label text="Instructions"></Label> <Label :text="'Instructions' | L"></Label>
</TabStripItem> </TabStripItem>
<TabStripItem> <TabStripItem>
<Label text="Combinations"></Label> <Label :text="'Combinations' | L"></Label>
</TabStripItem> </TabStripItem>
<TabStripItem> <TabStripItem>
<Label text="Notes"></Label> <Label :text="'Notes' | L"></Label>
</TabStripItem> </TabStripItem>
</TabStrip> </TabStrip>
<TabContentItem> <TabContentItem>
<ScrollView @scroll="onScroll"> <ScrollView @scroll="onScroll">
<StackLayout> <StackLayout>
<StackLayout <StackLayout width="100%" :height="screenWidth" verticalAlignment="center" class="imageHolder">
width="100%" <Image v-if="recipe.imageSrc" :src="recipe.imageSrc" stretch="aspectFill" width="100%" :height="screenWidth" />
:height="screenWidth" <Label v-else horizontalAlignment="center" class="bx" fontSize="160" :text="icon.image" />
verticalAlignment="center"
class="imageHolder"
>
<Image
v-if="recipe.imageSrc"
:src="recipe.imageSrc"
stretch="aspectFill"
width="100%"
:height="screenWidth"
/>
<Label
v-else
horizontalAlignment="center"
class="bx"
fontSize="160"
:text="icon.image"
/>
</StackLayout> </StackLayout>
<StackLayout margin="16 8 80"> <StackLayout margin="16 8 80">
<Label class="category orkm" :text="recipe.category" /> <Label class="category orkm" :text="`${recipe.category}` | L" />
<Label <Label margin="0 8" class="title orkm" :text="recipe.title" textWrap="true" />
margin="0 8" <Label class="time" :text="`${$options.filters.L('Preparation time')}: ${formattedTime(recipe.prepTime)}`" textWrap="true" />
class="title orkm" <Label class="time" :text="`${$options.filters.L('Cooking time')}: ${formattedTime(recipe.cookTime)}`" textWrap="true" />
:text="recipe.title" <GridLayout rows="auto, auto" columns="*, *" class="overviewContainer">
textWrap="true" <GridLayout class="overviewItem" row="0" col="0" rows="auto, auto" columns="*">
/>
<StackLayout orientation="horizontal" class="time">
<Label text="Preparation time:" />
<Label :text="` ${formattedTime(recipe.prepTime)}`" />
</StackLayout>
<StackLayout orientation="horizontal" class="time">
<Label text="Cooking time:" />
<Label :text="` ${formattedTime(recipe.cookTime)}`" />
</StackLayout>
<GridLayout
rows="auto, auto"
columns="*, *"
class="overviewContainer"
>
<GridLayout
class="overviewItem"
row="0"
col="0"
rows="auto, auto"
columns="*"
>
<MDRipple rowSpan="2" @tap="selectedTabIndex = 1" /> <MDRipple rowSpan="2" @tap="selectedTabIndex = 1" />
<Label row="0" class="bx" :text="icon.item" /> <Label row="0" class="bx" :text="icon.item" />
<Label <Label row="1" class="itemCount" :text="
row="1"
class="itemCount"
:text="
`${recipe.ingredients.length} ${ `${recipe.ingredients.length} ${
recipe.ingredients.length == 1 recipe.ingredients.length == 1
? 'Ingredient' ? $options.filters.L('Ingredient')
: 'Ingredients' : $options.filters.L('Ingredients')
}` }`
" " textWrap="true" />
textWrap="true"
/>
</GridLayout> </GridLayout>
<GridLayout <GridLayout class="overviewItem" row="0" col="1" rows="auto, auto" columns="*">
class="overviewItem"
row="0"
col="1"
rows="auto, auto"
columns="*"
>
<MDRipple rowSpan="2" @tap="selectedTabIndex = 2" /> <MDRipple rowSpan="2" @tap="selectedTabIndex = 2" />
<Label row="0" class="bx" :text="icon.step" /> <Label row="0" class="bx" :text="icon.step" />
<Label <Label row="1" class="itemCount" :text="
row="1"
class="itemCount"
:text="
`${recipe.instructions.length} ${ `${recipe.instructions.length} ${
recipe.instructions.length == 1 recipe.instructions.length == 1
? 'Instruction' ? $options.filters.L('Instruction')
: 'Instructions' : $options.filters.L('Instructions')
}` }`
" " textWrap="true" />
textWrap="true"
/>
</GridLayout> </GridLayout>
<GridLayout <GridLayout class="overviewItem" row="1" col="0" rows="auto, auto" columns="*">
class="overviewItem"
row="1"
col="0"
rows="auto, auto"
columns="*"
>
<MDRipple rowSpan="2" @tap="selectedTabIndex = 3" /> <MDRipple rowSpan="2" @tap="selectedTabIndex = 3" />
<Label row="0" class="bx" :text="icon.outline" /> <Label row="0" class="bx" :text="icon.outline" />
<Label <Label row="1" class="itemCount" :text="
row="1"
class="itemCount"
:text="
`${recipe.combinations.length} ${ `${recipe.combinations.length} ${
recipe.combinations.length == 1 recipe.combinations.length == 1
? 'Combination' ? $options.filters.L('Combination')
: 'Combinations' : $options.filters.L('Combinations')
}` }`
" " textWrap="true" />
textWrap="true"
/>
</GridLayout> </GridLayout>
<GridLayout <GridLayout class="overviewItem" row="1" col="1" rows="auto, auto" columns="*">
class="overviewItem"
row="1"
col="1"
rows="auto, auto"
columns="*"
>
<MDRipple rowSpan="2" @tap="selectedTabIndex = 4" /> <MDRipple rowSpan="2" @tap="selectedTabIndex = 4" />
<Label row="0" class="bx" :text="icon.note" /> <Label row="0" class="bx" :text="icon.note" />
<Label <Label row="1" class="itemCount" :text="
row="1"
class="itemCount"
:text="
`${recipe.notes.length} ${ `${recipe.notes.length} ${
recipe.notes.length == 1 ? 'Note' : 'Notes' recipe.notes.length == 1
? $options.filters.L('Note')
: $options.filters.L('Notes')
}` }`
" " textWrap="true" />
textWrap="true"
/>
</GridLayout> </GridLayout>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
@ -220,166 +95,75 @@
</TabContentItem> </TabContentItem>
<TabContentItem> <TabContentItem>
<ScrollView @scroll="onScroll"> <ScrollView @scroll="onScroll">
<GridLayout <GridLayout v-if="!recipe.ingredients.length" rows="*, auto, *, 88" columns="*" class="emptyStateContainer">
v-if="!recipe.ingredients.length"
rows="*, auto, *, 88"
columns="*"
class="emptyStateContainer"
>
<StackLayout col="0" row="1" class="emptyState"> <StackLayout col="0" row="1" class="emptyState">
<Label class="bx icon" :text="icon.item" textWrap="true" /> <Label class="bx icon" :text="icon.item" textWrap="true" />
<StackLayout orientation="horizontal" class="title orkm"> <Label class="subTitle orkm" :text="'Use the pencil button to add some ingredients' | L" textWrap="true" />
<Label text="Use the " />
<Label class="bx" :text="icon.edit" />
<Label text=" button" />
</StackLayout>
<Label text="to add some ingredients" textWrap="true" />
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
<StackLayout v-else padding="24 16 72"> <StackLayout v-else padding="24 16 72">
<AbsoluteLayout class="inputField"> <AbsoluteLayout class="inputField">
<TextField <TextField width="50%" v-model="yieldMultiplier" keyboardType="number" />
width="50%" <Label top="0" class="fieldLabel" :text="`${$options.filters.L('Required')} ${$options.filters.L(recipe.yield.unit)}`" />
v-model="yieldMultiplier"
keyboardType="number"
/>
<Label
top="0"
class="fieldLabel"
:text="`Required ${recipe.yield.unit.toLowerCase()}`"
/>
</AbsoluteLayout> </AbsoluteLayout>
<StackLayout margin="16 0"> <Label margin="16 0" class="subTitle orkm" :text="
<Label `${$options.filters.L('Ingredients')} (${positiveYieldMultiplier} ${$options.filters.L(recipe.yield.unit)})`
class="title orkm" " textWrap="true" />
:text=" <StackLayout v-for="(item, index) in recipe.ingredients" :key="index">
`Ingredients for ${positiveYieldMultiplier} ${recipe.yield.unit.toLowerCase()}` <check-box class="ingredient" checkPadding="16" fillColor="#ff5200" :text="
"
textWrap="true"
/>
</StackLayout>
<StackLayout
v-for="(item, index) in recipe.ingredients"
:key="index"
>
<check-box
class="ingredient"
checkPadding="16"
fillColor="#ff5200"
:text="
`${ `${
roundedQuantity(item.quantity) roundedQuantity(item.quantity)
? roundedQuantity(item.quantity) + ' ' ? roundedQuantity(item.quantity) + ' '
: '' : ''
}${roundedQuantity(item.quantity) ? item.unit + ' ' : ''}${ }${roundedQuantity(item.quantity) ? $options.filters.L(item.unit) + ' ' : ''}${
item.item item.item
}` }`
" " />
/>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
</TabContentItem> </TabContentItem>
<TabContentItem> <TabContentItem>
<ScrollView @scroll="onScroll"> <ScrollView @scroll="onScroll">
<GridLayout <GridLayout v-if="!recipe.instructions.length" rows="*, auto, *, 88" columns="*" class="emptyStateContainer">
v-if="!recipe.instructions.length"
rows="*, auto, *, 88"
columns="*"
class="emptyStateContainer"
>
<StackLayout col="0" row="1" class="emptyState"> <StackLayout col="0" row="1" class="emptyState">
<Label class="bx icon" :text="icon.step" textWrap="true" /> <Label class="bx icon" :text="icon.step" textWrap="true" />
<StackLayout orientation="horizontal" class="title orkm"> <Label class="subTitle orkm" :text="'Use the pencil button to add some instructions' | L" textWrap="true" />
<Label text="Use the " />
<Label class="bx" :text="icon.edit" />
<Label text=" button" />
</StackLayout>
<Label text="to add some instructions" textWrap="true" />
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
<StackLayout v-else padding="28 16 62"> <StackLayout v-else padding="28 16 62">
<GridLayout <GridLayout columns="auto ,*" v-for="(instruction, index) in recipe.instructions" :key="index">
columns="auto ,*" <Label col="0" colSpan="2" class="instruction" :class="{
v-for="(instruction, index) in recipe.instructions"
:key="index"
>
<Label
col="0"
colSpan="2"
class="instruction"
:class="{
noBorder: index === recipe.instructions.length - 1, noBorder: index === recipe.instructions.length - 1,
}" }" :text="instruction" textWrap="true" />
:text="instruction" <Label verticalAlignment="top" horizontalAlignment="center" class="count orkm" col="0" :text="index + 1" />
textWrap="true"
/>
<Label
verticalAlignment="top"
horizontalAlignment="center"
class="count orkm"
col="0"
:text="index + 1"
/>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
</TabContentItem> </TabContentItem>
<TabContentItem> <TabContentItem>
<ScrollView @scroll="onScroll"> <ScrollView @scroll="onScroll">
<GridLayout <GridLayout v-if="!recipe.combinations.length" rows="*, auto, *, 88" columns="*" class="emptyStateContainer">
v-if="!recipe.combinations.length"
rows="*, auto, *, 88"
columns="*"
class="emptyStateContainer"
>
<StackLayout col="0" row="1" class="emptyState"> <StackLayout col="0" row="1" class="emptyState">
<Label class="bx icon" :text="icon.outline" textWrap="true" /> <Label class="bx icon" :text="icon.outline" textWrap="true" />
<StackLayout orientation="horizontal" class="title orkm"> <Label class="subTitle orkm" :text="'Use the pencil button to add some combinations' | L" textWrap="true" />
<Label text="Use the " />
<Label class="bx" :text="icon.edit" />
<Label text=" button" />
</StackLayout>
<Label text="to add some combinations" textWrap="true" />
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
<StackLayout v-else padding="8 0 80"> <StackLayout v-else padding="8 0 80">
<GridLayout <GridLayout columns="auto, *" v-for="(combination, index) in recipe.combinations" :key="index" androidElevation="1" class="urlCard">
columns="auto, *"
v-for="(combination, index) in recipe.combinations"
:key="index"
androidElevation="1"
class="urlCard"
>
<MDRipple colSpan="2" @tap="viewCombination(combination)" /> <MDRipple colSpan="2" @tap="viewCombination(combination)" />
<Label col="0" class="bx linkIcon" :text="icon.food" /> <Label col="0" class="bx linkIcon" :text="icon.food" />
<Label <Label col="1" verticalAlignment="center" class="link" :text="getCombinationTitle(combination)" textWrap="true" />
col="1"
verticalAlignment="center"
class="link"
:text="getCombinationTitle(combination)"
textWrap="true"
/>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
</TabContentItem> </TabContentItem>
<TabContentItem> <TabContentItem>
<ScrollView @scroll="onScroll"> <ScrollView @scroll="onScroll">
<GridLayout <GridLayout v-if="!recipe.notes.length" rows="*, auto, *, 88" columns="*" class="emptyStateContainer">
v-if="!recipe.notes.length"
rows="*, auto, *, 88"
columns="*"
class="emptyStateContainer"
>
<StackLayout col="0" row="1" class="emptyState"> <StackLayout col="0" row="1" class="emptyState">
<Label class="bx icon" :text="icon.note" textWrap="true" /> <Label class="bx icon" :text="icon.note" textWrap="true" />
<StackLayout orientation="horizontal" class="title orkm"> <Label class="subTitle orkm" :text="'Use the pencil button to add some notes' | L" textWrap="true" />
<Label text="Use the " />
<Label class="bx" :text="icon.edit" />
<Label text=" button" />
</StackLayout>
<Label text="to add some notes" textWrap="true" />
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
<StackLayout v-else padding="8 0 80"> <StackLayout v-else padding="8 0 80">
@ -390,13 +174,7 @@
</Tabs> </Tabs>
<GridLayout id="btnFabContainer" rows="*, auto" columns="*, auto"> <GridLayout id="btnFabContainer" rows="*, auto" columns="*, auto">
<transition name="dolly" appear> <transition name="dolly" appear>
<MDFloatingActionButton <MDFloatingActionButton row="1" col="1" src="res://share" @tap="shareHandler" v-if="showFab" />
row="1"
col="1"
src="res://share"
@tap="shareHandler"
v-if="showFab"
/>
</transition> </transition>
</GridLayout> </GridLayout>
</AbsoluteLayout> </AbsoluteLayout>
@ -413,18 +191,30 @@ import {
Span, Span,
FormattedString, FormattedString,
Label, Label,
} from "@nativescript/core" Observable
import { Feedback, FeedbackType, FeedbackPosition } from "nativescript-feedback" }
from "@nativescript/core"
import {
Feedback,
FeedbackType,
FeedbackPosition
}
from "nativescript-feedback"
import * as Toast from "nativescript-toast" import * as Toast from "nativescript-toast"
import * as SocialShare from "@nativescript/social-share" import * as SocialShare from "@nativescript/social-share"
import { mapActions, mapState } from "vuex" import {
localize
}
from "@nativescript/localize"
import {
mapActions,
mapState
}
from "vuex"
import EditRecipe from "./EditRecipe.vue" import EditRecipe from "./EditRecipe.vue"
import ViewRecipe from "./ViewRecipe.vue" import ViewRecipe from "./ViewRecipe.vue"
import ShareChooser from "./modal/ShareChooser.vue" import ShareChooser from "./modal/ShareChooser.vue"
let feedback = new Feedback() let feedback = new Feedback()
export default { export default {
props: [ "filterTrylater", "recipeID" ], props: [ "filterTrylater", "recipeID" ],
data() { data() {
@ -446,22 +236,17 @@ export default {
return Screen.mainScreen.widthDIPs return Screen.mainScreen.widthDIPs
}, },
positiveYieldMultiplier() { positiveYieldMultiplier() {
return Math.abs(this.yieldMultiplier) > 0 return Math.abs( this.yieldMultiplier ) > 0 ? Math.abs( parseFloat( this.yieldMultiplier ) ) : 1
? Math.abs(parseFloat(this.yieldMultiplier))
: 1
}, },
isLightMode() { isLightMode() {
return Application.systemAppearance() === "light" return Application.systemAppearance() === "light"
}, },
}, },
methods: { methods: {
...mapActions([ ...mapActions( [ "toggleStateAction", "setCurrentComponentAction", "overwriteRecipeAction", "setRecipeAsTriedAction", ] ),
"toggleStateAction", onPageLoad( args ) {
"setCurrentComponentAction", const page = args.object;
"overwriteRecipeAction", page.bindingContext = new Observable();
"setRecipeAsTriedAction",
]),
onPageLoad() {
this.busy = false this.busy = false
setTimeout( ( e ) => { setTimeout( ( e ) => {
this.setCurrentComponentAction( "ViewRecipe" ) this.setCurrentComponentAction( "ViewRecipe" )
@ -475,7 +260,6 @@ export default {
feedback.hide() feedback.hide()
this.keepScreenOn( false ) this.keepScreenOn( false )
}, },
// HELPERS // HELPERS
niceDates( time ) { niceDates( time ) {
let lastTried = new Date( time ).getTime() let lastTried = new Date( time ).getTime()
@ -484,14 +268,16 @@ export default {
let diff = ( now - lastTried ) / 1000 let diff = ( now - lastTried ) / 1000
let dayDiff = Math.ceil( diff / 86400 ) let dayDiff = Math.ceil( diff / 86400 )
if ( isNaN( dayDiff ) || dayDiff < 0 ) return "" if ( isNaN( dayDiff ) || dayDiff < 0 ) return ""
function duration( value ) {
return localize( value )
}
return ( return (
(diff < 86400 && lastTried > midnight && "today") || ( diff < 86400 && lastTried > midnight && duration( "today" ) ) || ( dayDiff == 1 && "yesterday" ) || ( dayDiff < 7 && dayDiff + " " + duration( "days ago" ) ) || ( dayDiff < 31 && Math.round( dayDiff / 7 ) + " " + duration(
(dayDiff == 1 && "yesterday") || "weeks ago" ) ) || ( dayDiff <
(dayDiff < 7 && dayDiff + " days ago") || 366 && Math.round(
(dayDiff < 31 && Math.round(dayDiff / 7) + " week(s) ago") || dayDiff /
(dayDiff < 366 && Math.round(dayDiff / 30) + " month(s) ago") || 30 ) + " " + duration( "months ago" ) ) || ( dayDiff > 365 && duration( "long time ago" ) ) )
(dayDiff > 365 && "long time ago")
)
}, },
selectedIndexChange( args ) { selectedIndexChange( args ) {
this.selectedTabIndex = args.object.selectedIndex this.selectedTabIndex = args.object.selectedIndex
@ -499,41 +285,30 @@ export default {
}, },
showLastTried() { showLastTried() {
feedback.show( { feedback.show( {
title: `You tried this recipe ${this.niceDates( title: `${localize('You tried this recipe:')} ${this.niceDates(
this.recipe.lastTried this.recipe.lastTried
)}!`, )}`,
titleColor: new Color( `${this.isLightMode ? "#f1f3f5" : "#212529"}` ), titleColor: new Color( `${this.isLightMode ? "#f1f3f5" : "#212529"}` ),
backgroundColor: new Color( "#ff5200" ), backgroundColor: new Color( "#ff5200" ),
} ) } )
}, },
roundedQuantity( quantity ) { roundedQuantity( quantity ) {
return Math.abs( return Math.abs( Math.round(
Math.round( ( quantity / this.recipe.yield.quantity ) * this.positiveYieldMultiplier * 100 ) / 100 )
(quantity / this.recipe.yield.quantity) *
this.positiveYieldMultiplier *
100
) / 100
)
}, },
keepScreenOn( boolean ) { keepScreenOn( boolean ) {
let activity = let activity = Application.android.foregroundActivity || Application.android.startActivity
Application.android.foregroundActivity ||
Application.android.startActivity
let window = activity.getWindow() let window = activity.getWindow()
if (boolean) if ( boolean ) window.addFlags( android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON )
window.addFlags( else window.clearFlags( android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON )
android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
)
else
window.clearFlags(
android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
)
}, },
formattedTime( time ) { formattedTime( time ) {
let t = time.split( ":" ) let t = time.split( ":" )
let h = parseInt( t[ 0 ] ) let h = parseInt( t[ 0 ] )
let m = parseInt( t[ 1 ] ) let m = parseInt( t[ 1 ] )
return h ? (m ? `${h} hr ${m} min` : `${h} hr`) : `${m} min` let hr = localize( 'hr' )
let min = localize( 'min' )
return h ? ( m ? `${h} ${hr} ${m} ${min}` : `${h} ${hr}` ) : `${m} ${min}`
}, },
isValidURL( string ) { isValidURL( string ) {
let pattern = new RegExp( "^https?|^www", "ig" ) let pattern = new RegExp( "^https?|^www", "ig" )
@ -543,24 +318,17 @@ export default {
return this.recipes.filter( ( e ) => e.id === id )[ 0 ].title return this.recipes.filter( ( e ) => e.id === id )[ 0 ].title
}, },
syncCombinations() { syncCombinations() {
let combinationForOtherRecipes = this.recipes let combinationForOtherRecipes = this.recipes.filter(
.filter( ( e ) => e.combinations.indexOf( this.currentRecipeID ) >= 0 || this.recipe.combinations.includes( e.id ) ).map( ( e ) => e.id )
(e) =>
e.combinations.indexOf(this.currentRecipeID) >= 0 ||
this.recipe.combinations.includes(e.id)
)
.map((e) => e.id)
this.recipe.combinations = combinationForOtherRecipes this.recipe.combinations = combinationForOtherRecipes
this.overwriteRecipeAction( { this.overwriteRecipeAction( {
id: this.currentRecipeID, id: this.currentRecipeID,
recipe: this.recipe, recipe: this.recipe,
} ) } )
}, },
// NAVIGATION HANDLERS // NAVIGATION HANDLERS
onScroll( args ) { onScroll( args ) {
this.viewIsScrolled = this.isScrolled[this.selectedTabIndex] = this.viewIsScrolled = this.isScrolled[ this.selectedTabIndex ] = args.scrollY > 8 ? true : false
args.scrollY > 8 ? true : false
}, },
editRecipe() { editRecipe() {
this.showFab = false this.showFab = false
@ -580,12 +348,8 @@ export default {
this.syncCombinations() this.syncCombinations()
this.selectedTabIndex = 0 this.selectedTabIndex = 0
setTimeout( setTimeout(
(e) => ( e ) => this.recipe.tried && this.recipe.lastTried && this.showLastTried(), 500 )
this.recipe.tried && this.recipe.lastTried && this.showLastTried(),
500
)
}, },
// SHARE ACTION // SHARE ACTION
shareHandler() { shareHandler() {
if ( this.recipe.imageSrc ) { if ( this.recipe.imageSrc ) {
@ -603,26 +367,26 @@ export default {
case "recipe": case "recipe":
this.shareRecipe() this.shareRecipe()
break break
default: default:
break break
} }
} ) } )
} else { }
else {
this.shareRecipe() this.shareRecipe()
} }
}, },
shareRecipe() { shareRecipe() {
let overview = `${ let overview = `${
this.recipe.title this.recipe.title
}\n\nPreparation time: ${this.formattedTime( }\n\n${localize("Preparation time")}: ${this.formattedTime(
this.recipe.prepTime this.recipe.prepTime
)}\nCooking time: ${this.formattedTime(this.recipe.cookTime)}\n` )}\n${localize( "Cooking time" )}: ${this.formattedTime(this.recipe.cookTime)}\n`
let shareContent = overview let shareContent = overview
if ( this.recipe.ingredients.length ) { if ( this.recipe.ingredients.length ) {
let ingredients = `\n\nIngredients for ${ let ingredients = `\n\n${localize( "Ingredients" )} (${
this.yieldMultiplier this.yieldMultiplier
} ${this.recipe.yield.unit.toLowerCase()}\n\n` } ${localize( this.recipe.yield.unit )})\n\n`
this.recipe.ingredients.forEach( ( e ) => { this.recipe.ingredients.forEach( ( e ) => {
ingredients += `- ${ ingredients += `- ${
e.quantity e.quantity
@ -633,34 +397,30 @@ export default {
shareContent += ingredients shareContent += ingredients
} }
if ( this.recipe.instructions.length ) { if ( this.recipe.instructions.length ) {
let instructions = `\n\nInstructions:\n\n` let instructions = `\n\n${localize( "Instructions" )}:\n\n`
this.recipe.instructions.forEach( ( e, i ) => { this.recipe.instructions.forEach( ( e, i ) => {
instructions += `Step ${i + 1}: ${e}\n\n` instructions += `${i + 1}. ${e}\n\n`
} ) } )
shareContent += instructions shareContent += instructions
} }
if ( this.recipe.combinations.length ) { if ( this.recipe.combinations.length ) {
let combinations = `\nCombinations:\n\n` let combinations = `\n${localize( "Combinations" )}:\n\n`
this.recipe.combinations.forEach( ( e, i ) => { this.recipe.combinations.forEach( ( e, i ) => {
combinations += `${i + 1}. ${this.getCombinationTitle(e)}\n\n` combinations += `${i + 1}. ${this.getCombinationTitle(e)}\n\n`
} ) } )
shareContent += combinations shareContent += combinations
} }
if ( this.recipe.notes.length ) { if ( this.recipe.notes.length ) {
let notes = `\nNotes:\n\n` let notes = `\n${localize( "Notes" )}:\n\n`
this.recipe.notes.forEach( ( e, i ) => { this.recipe.notes.forEach( ( e, i ) => {
notes += `${i + 1}. ${e}\n\n` notes += `${i + 1}. ${e}\n\n`
} ) } )
shareContent += notes shareContent += notes
} }
let sharenote = let sharenote = '\n' + localize( "Shared via EnRecipes. Get it on Play Store or F-Droid." )
"\nCreated and shared via EnRecipes. Get it on Play Store or F-Droid."
shareContent += sharenote shareContent += sharenote
SocialShare.shareText( shareContent, "Share recipe using" ) SocialShare.shareText( shareContent, "Share recipe using" )
}, },
// DATA HANDLERS // DATA HANDLERS
toggle( key, setDate ) { toggle( key, setDate ) {
this.toggleStateAction( { this.toggleStateAction( {
@ -670,20 +430,13 @@ export default {
setDate, setDate,
} ) } )
}, },
toggleFavorite() { toggleFavourite() {
this.recipe.isFavorite this.recipe.isFavorite ? Toast.makeText( localize( "Removed from Favourites" ) ).show() : Toast.makeText( localize( "Added to Favourites" ) ).show()
? Toast.makeText("Removed from Favorites").show()
: Toast.makeText("Added to Favorites").show()
this.toggle( "isFavorite" ) this.toggle( "isFavorite" )
}, },
toggleTrylater() { toggleTrylater() {
this.recipe.tried this.recipe.tried ? Toast.makeText( localize( "Added to Try Later" ) ).show() : Toast.makeText(
? this.filterTrylater localize( "Removed from Try Later" ) ).show()
? Toast.makeText("Added back to Try Later").show()
: Toast.makeText("Added to Try Later").show()
: this.filterTrylater
? Toast.makeText("You tried this recipe").show()
: Toast.makeText("Removed from Try Later").show()
this.toggle( "tried" ) this.toggle( "tried" )
}, },
recipeTried() { recipeTried() {
@ -693,7 +446,6 @@ export default {
} ) } )
this.$navigateBack() this.$navigateBack()
}, },
// NOTES // NOTES
createNote( note ) { createNote( note ) {
const vm = this const vm = this
@ -715,13 +467,10 @@ export default {
} }
formattedString.spans.push( span ) formattedString.spans.push( span )
} }
textArray.forEach( ( text ) => { textArray.forEach( ( text ) => {
createSpan( text, regex.test( text ) ) createSpan( text, regex.test( text ) )
} ) } )
label.formattedText = formattedString label.formattedText = formattedString
return label return label
}, },
createNotes( args ) { createNotes( args ) {
@ -739,9 +488,7 @@ export default {
mounted() { mounted() {
this.showFab = true this.showFab = true
setTimeout( setTimeout(
(e) => this.recipe.tried && this.recipe.lastTried && this.showLastTried(), ( e ) => this.recipe.tried && this.recipe.lastTried && this.showLastTried(), 500 )
500
)
}, },
} }
</script> </script>

View file

@ -1,46 +1,25 @@
<template> <template>
<Page> <Page>
<StackLayout class="dialogContainer" :class="appTheme"> <StackLayout class="dialogContainer" :class="appTheme">
<Label class="dialogTitle orkm" :text="title" /> <Label class="dialogTitle orkm" :text="`${title}` | L" />
<ScrollView width="100%" :height="height ? height : screenHeight - 256"> <ScrollView width="100%" :height="height ? height : screenHeight - 256">
<StackLayout> <StackLayout>
<MDButton <MDButton v-for="(item, index) in list" :key="index" class="actionItem" variant="text" :rippleColor="rippleColor" :text="`${item}` | L" @loaded="onLabelLoaded" @tap="tapAction(item)" />
v-for="(item, index) in list"
:key="index"
class="actionItem"
variant="text"
:rippleColor="rippleColor"
:text="item"
@loaded="onLabelLoaded"
@tap="tapAction(item)"
/>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
<GridLayout rows="auto" columns="auto, *, auto" class="actionsContainer"> <GridLayout rows="auto" columns="auto, *, auto" class="actionsContainer">
<MDButton <MDButton :rippleColor="rippleColor" variant="text" v-if="action" col="0" class="action orkm pull-left" :text="`${action}` | L" @tap="$modal.close(action)" />
:rippleColor="rippleColor" <MDButton :rippleColor="rippleColor" variant="text" col="2" class="action orkm pull-right" :text="'CANCEL' | L" @tap="$modal.close(false)" />
variant="text"
v-if="action"
col="0"
class="action orkm pull-left"
:text="action"
@tap="$modal.close(action)"
/>
<MDButton
:rippleColor="rippleColor"
variant="text"
col="2"
class="action orkm pull-right"
text="CANCEL"
@tap="$modal.close(false)"
/>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</Page> </Page>
</template> </template>
<script> <script>
import { Application, Screen } from "@nativescript/core" import {
Application,
Screen
} from "@nativescript/core"
export default { export default {
props: [ "title", "list", "height", "action" ], props: [ "title", "list", "height", "action" ],
computed: { computed: {
@ -48,9 +27,7 @@ export default {
return Application.systemAppearance() return Application.systemAppearance()
}, },
rippleColor() { rippleColor() {
return this.appTheme == "light" return this.appTheme == "light" ? "rgba(134,142,150,0.2)" : "rgba(206,212,218,0.1)"
? "rgba(134,142,150,0.2)"
: "rgba(206,212,218,0.1)"
}, },
screenHeight() { screenHeight() {
return Math.round( Screen.mainScreen.heightDIPs ) return Math.round( Screen.mainScreen.heightDIPs )

View file

@ -1,60 +1,30 @@
<template> <template>
<Page> <Page>
<StackLayout class="dialogContainer" :class="appTheme"> <StackLayout class="dialogContainer" :class="appTheme">
<Label class="dialogTitle orkm" :text="title" /> <Label class="dialogTitle orkm" :text="`${title}` | L" textWrap='true' />
<StackLayout <StackLayout v-if="filteredRecipes.length || searchTerm" padding="0 24 24">
v-if="filteredRecipes.length || searchTerm" <TextField :hint="'Search' | L" v-model="searchTerm" />
padding="0 24 24"
>
<TextField hint="Search" v-model="searchTerm" />
</StackLayout> </StackLayout>
<ScrollView width="100%" :height="height ? height : screenHeight - 320"> <ScrollView width="100%" :height="height ? height : screenHeight - 320">
<StackLayout> <StackLayout>
<MDButton <MDButton v-for="(recipe, index) in filteredRecipes" :key="index" class="actionItem" variant="text" :rippleColor="rippleColor" :text="recipe.title" @loaded="onLabelLoaded" @tap="tapAction(recipe)" />
v-for="(recipe, index) in filteredRecipes" <Label padding="24" lineHeight="6" v-if="!filteredRecipes.length" :text="'Nothing here! Add some recipes and try again.' | L" textAlignment="center" textWrap="true" />
:key="index"
class="actionItem"
variant="text"
:rippleColor="rippleColor"
:text="recipe.title"
@loaded="onLabelLoaded"
@tap="tapAction(recipe)"
/>
<Label
padding="24"
lineHeight="6"
v-if="!filteredRecipes.length"
text="Nothing here! Add some recipes and try again."
textAlignment="center"
textWrap="true"
/>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
<GridLayout rows="auto" columns="auto, *, auto" class="actionsContainer"> <GridLayout rows="auto" columns="auto, *, auto" class="actionsContainer">
<MDButton <MDButton :rippleColor="rippleColor" variant="text" v-if="action" col="0" class="action orkm pull-left" :text="`${action}` | L" @tap="$modal.close(action)" />
:rippleColor="rippleColor" <MDButton :rippleColor="rippleColor" variant="text" col="2" class="action orkm pull-right" :text="'CANCEL' | L" @tap="$modal.close(false)" />
variant="text"
v-if="action"
col="0"
class="action orkm pull-left"
:text="action"
@tap="$modal.close(action)"
/>
<MDButton
:rippleColor="rippleColor"
variant="text"
col="2"
class="action orkm pull-right"
text="CANCEL"
@tap="$modal.close(false)"
/>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</Page> </Page>
</template> </template>
<script> <script>
import { Application, Screen } from "@nativescript/core" import {
Application,
Screen
}
from "@nativescript/core"
export default { export default {
props: [ "title", "recipes", "height", "action" ], props: [ "title", "recipes", "height", "action" ],
data() { data() {
@ -67,22 +37,18 @@ export default {
return Application.systemAppearance() return Application.systemAppearance()
}, },
rippleColor() { rippleColor() {
return this.appTheme == "light" return this.appTheme == "light" ? "rgba(134,142,150,0.2)" : "rgba(206,212,218,0.1)"
? "rgba(134,142,150,0.2)"
: "rgba(206,212,218,0.1)"
}, },
screenHeight() { screenHeight() {
return Math.round( Screen.mainScreen.heightDIPs ) return Math.round( Screen.mainScreen.heightDIPs )
}, },
filteredRecipes() { filteredRecipes() {
return this.recipes return this.recipes.map( ( e, i ) => {
.map((e, i) => {
return { return {
id: e.id, id: e.id,
title: e.title, title: e.title,
} }
}) } ).filter( ( e ) => e.title.includes( this.searchTerm ) )
.filter((e) => e.title.includes(this.searchTerm))
}, },
}, },
methods: { methods: {

View file

@ -1,37 +1,21 @@
<template> <template>
<Page> <Page>
<StackLayout class="dialogContainer" :class="appTheme"> <StackLayout class="dialogContainer" :class="appTheme">
<Label class="dialogTitle orkm" :text="title" textWrap="true"/> <Label class="dialogTitle orkm" :text="`${title}` | L" textWrap="true" />
<Label <Label v-if="description" class="dialogDescription" :text="description" textWrap="true" />
v-if="description"
class="dialogDescription"
:text="description"
textWrap="true"
/>
<GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer"> <GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
<MDButton <MDButton :rippleColor="rippleColor" variant="text" col="1" class="action orkm" :text="`${cancelButtonText}` | L" @tap="$modal.close(false)" />
:rippleColor="rippleColor" <MDButton :rippleColor="rippleColor" variant="text" col="2" class="action orkm" :text="`${okButtonText}` | L" @tap="$modal.close(true)" />
variant="text"
col="1"
class="action orkm"
:text="cancelButtonText"
@tap="$modal.close(false)"
/>
<MDButton
:rippleColor="rippleColor"
variant="text"
col="2"
class="action orkm"
:text="okButtonText"
@tap="$modal.close(true)"
/>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</Page> </Page>
</template> </template>
<script> <script>
import { Application } from "@nativescript/core" import {
Application
}
from "@nativescript/core"
export default { export default {
props: [ "title", "description", "cancelButtonText", "okButtonText" ], props: [ "title", "description", "cancelButtonText", "okButtonText" ],
computed: { computed: {
@ -39,9 +23,9 @@ export default {
return Application.systemAppearance() return Application.systemAppearance()
}, },
rippleColor() { rippleColor() {
return this.appTheme == "light" return this.appTheme == "light" ?
? "rgba(134,142,150,0.2)" "rgba(134,142,150,0.2)" :
: "rgba(206,212,218,0.1)" "rgba(206,212,218,0.1)"
}, },
}, },
} }

View file

@ -1,125 +1,60 @@
<template> <template>
<Page> <Page>
<StackLayout class="dialogContainer" :class="appTheme"> <StackLayout class="dialogContainer" :class="appTheme">
<Label class="dialogTitle orkm" :text="title" /> <Label class="dialogTitle orkm" :text="`${title}` | L" />
<StackLayout <StackLayout class="dialogListPicker" orientation="horizontal" horizontalAlignment="center">
class="dialogListPicker" <ListPicker ref="hrPicker" :items="hrsList" :selectedIndex="hrIndex" @selectedIndexChange="setHrs"></ListPicker>
orientation="horizontal" <ListPicker ref="minPicker" :items="minsList" :selectedIndex="minIndex" @selectedIndexChange="setMins"></ListPicker>
horizontalAlignment="center"
>
<ListPicker
ref="hrPicker"
:items="hrs"
:selectedIndex="hrIndex"
@selectedIndexChange="setHrs"
></ListPicker>
<ListPicker
ref="minPicker"
:items="mins"
:selectedIndex="minIndex"
@selectedIndexChange="setMins"
></ListPicker>
</StackLayout> </StackLayout>
<GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer"> <GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
<MDButton <MDButton :rippleColor="rippleColor" variant="text" col="1" class="action orkm" :text="'CANCEL' | L" @tap="$modal.close(false)" />
:rippleColor="rippleColor" <MDButton :rippleColor="rippleColor" variant="text" col="2" class="action orkm" :text="`${action}` | L" @tap="$modal.close(selectedTime)" />
variant="text"
col="1"
class="action orkm"
text="CANCEL"
@tap="$modal.close(false)"
/>
<MDButton
:rippleColor="rippleColor"
variant="text"
col="2"
class="action orkm"
:text="action"
@tap="$modal.close(selectedTime)"
/>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</Page> </Page>
</template> </template>
<script> <script>
import { Application } from "@nativescript/core" import {
Application
}
from "@nativescript/core"
import {
localize
}
from "@nativescript/localize"
export default { export default {
props: [ "title", "selectedHr", "selectedMin", "action" ], props: [ "title", "selectedHr", "selectedMin", "action" ],
data() { data() {
return { return {
hrs: [ hrs: [],
"0 hr", mins: [],
"1 hr",
"2 hr",
"3 hr",
"4 hr",
"5 hr",
"6 hr",
"7 hr",
"8 hr",
"9 hr",
"10 hr",
"11 hr",
"12 hr",
"13 hr",
"14 hr",
"15 hr",
"16 hr",
"17 hr",
"18 hr",
"19 hr",
"20 hr",
"21 hr",
"22 hr",
"23 hr",
],
mins: [
"0 min",
"1 min",
"2 min",
"3 min",
"4 min",
"5 min",
"6 min",
"7 min",
"8 min",
"9 min",
"10 min",
"15 min",
"20 min",
"25 min",
"30 min",
"35 min",
"40 min",
"45 min",
"50 min",
"55 min",
],
selectedHrs: "00", selectedHrs: "00",
selectedMins: "00", selectedMins: "00",
} }
}, },
computed: { computed: {
hrsList() {
let h = [ ...Array( 24 ).keys() ]
this.hrs = h
return h.map( e => `${e} ${localize( 'hr' )}` )
},
minsList() {
let m = [ ...new Set( [ ...Array( 11 ).keys(), ...Array.from( Array( 12 ), ( _, x ) => x * 5 ) ] ) ]
this.mins = m
return m.map( e => `${e} ${localize( 'min' )}` )
},
hrIndex() { hrIndex() {
let hr = this.selectedHr return this.hrs.indexOf( parseInt( this.selectedHr ) )
if (hr.charAt(0) == "0") hr = hr.slice(-1) + " hr"
else hr = hr + " hr"
return this.hrs.indexOf(hr)
}, },
minIndex() { minIndex() {
let min = this.selectedMin return this.mins.indexOf( parseInt( this.selectedMin ) )
if (min.charAt(0) == "0") min = min.slice(-1) + " min"
else min = min + " min"
return this.mins.indexOf(min)
}, },
appTheme() { appTheme() {
return Application.systemAppearance() return Application.systemAppearance()
}, },
rippleColor() { rippleColor() {
return this.appTheme == "light" return this.appTheme == "light" ? "rgba(134,142,150,0.2)" : "rgba(206,212,218,0.1)"
? "rgba(134,142,150,0.2)"
: "rgba(206,212,218,0.1)"
}, },
selectedTime() { selectedTime() {
return this.selectedHrs + ":" + this.selectedMins return this.selectedHrs + ":" + this.selectedMins
@ -128,11 +63,11 @@ export default {
methods: { methods: {
setHrs( args ) { setHrs( args ) {
let hr = "0" + this.hrs[ args.object.selectedIndex ] let hr = "0" + this.hrs[ args.object.selectedIndex ]
this.selectedHrs = hr.slice(-5).slice(0, -3) this.selectedHrs = hr.slice( -2 )
}, },
setMins( args ) { setMins( args ) {
let min = "0" + this.mins[ args.object.selectedIndex ] let min = "0" + this.mins[ args.object.selectedIndex ]
this.selectedMins = min.slice(-6).slice(0, -4) this.selectedMins = min.slice( -2 )
}, },
}, },
} }

View file

@ -1,39 +1,28 @@
<template> <template>
<Page> <Page>
<StackLayout class="dialogContainer" :class="appTheme"> <StackLayout class="dialogContainer" :class="appTheme">
<Label class="dialogTitle orkm" :text="title" /> <Label class="dialogTitle orkm" :text="`${title}` | L" textWrap='true' />
<StackLayout class="dialogInput"> <StackLayout class="dialogInput">
<TextField <TextField @loaded="focusField" :hint="hint ? hint : ''" v-model="category" autocapitalizationType="words" />
@loaded="focusField"
:hint="hint ? hint : ''"
v-model="category"
autocapitalizationType="words"
/>
</StackLayout> </StackLayout>
<GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer"> <GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
<MDButton <MDButton :rippleColor="rippleColor" variant="text" col="1" class="action orkm" :text="'CANCEL' | L" @tap="$modal.close(false)" />
:rippleColor="rippleColor" <MDButton :rippleColor="rippleColor" variant="text" col="2" class="action orkm" :text="`${action}` | L" @tap="$modal.close(category)" />
variant="text"
col="1"
class="action orkm"
text="CANCEL"
@tap="$modal.close(false)"
/>
<MDButton
:rippleColor="rippleColor"
variant="text"
col="2"
class="action orkm"
:text="action"
@tap="$modal.close(category)"
/>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</Page> </Page>
</template> </template>
<script> <script>
import { Application, Utils } from "@nativescript/core" import {
Application,
Utils
}
from "@nativescript/core"
import {
localize
}
from '@nativescript/localize'
export default { export default {
props: [ "title", "hint", "text", "action" ], props: [ "title", "hint", "text", "action" ],
data() { data() {
@ -46,9 +35,7 @@ export default {
return Application.systemAppearance() return Application.systemAppearance()
}, },
rippleColor() { rippleColor() {
return this.appTheme == "light" return this.appTheme == "light" ? "rgba(134,142,150,0.2)" : "rgba(206,212,218,0.1)"
? "rgba(134,142,150,0.2)"
: "rgba(206,212,218,0.1)"
}, },
}, },
methods: { methods: {
@ -59,7 +46,7 @@ export default {
}, },
mounted() { mounted() {
if ( this.text ) { if ( this.text ) {
this.category = this.text this.category = localize( this.text )
} }
}, },
} }

View file

@ -1,34 +1,22 @@
<template> <template>
<Page> <Page>
<StackLayout class="dialogContainer" :class="appTheme"> <StackLayout class="dialogContainer" :class="appTheme">
<Label class="dialogTitle orkm" :text="title" /> <Label class="dialogTitle orkm" :text="`${title}` | L" />
<GridLayout rows="auto, auto" columns="*" class="actionsContainer"> <GridLayout rows="auto, auto" columns="*" class="actionsContainer">
<MDButton <MDButton :rippleColor="rippleColor" :backgroundColor="backgroundColor" row="0" class="actionIcon" src="res://photo" :text="'Photo' | L" @tap="$modal.close('photo')" />
:rippleColor="rippleColor" <MDButton :rippleColor="rippleColor" :backgroundColor="backgroundColor" row="1" class="actionIcon" src="res://detail" :text="'Recipe' | L" @tap="$modal.close('recipe')" />
:backgroundColor="backgroundColor"
row="0"
class="actionIcon"
src="res://photo"
text="Photo"
@tap="$modal.close('photo')"
/>
<MDButton
:rippleColor="rippleColor"
:backgroundColor="backgroundColor"
row="1"
class="actionIcon"
src="res://detail"
text="Recipe"
@tap="$modal.close('recipe')"
/>
</GridLayout> </GridLayout>
</StackLayout> </StackLayout>
</Page> </Page>
</template> </template>
<script> <script>
import { Application } from "@nativescript/core" import {
import { mapState } from "vuex" Application
} from "@nativescript/core"
import {
mapState
} from "vuex"
export default { export default {
props: [ "title" ], props: [ "title" ],
computed: { computed: {
@ -37,9 +25,7 @@ export default {
return Application.systemAppearance() return Application.systemAppearance()
}, },
rippleColor() { rippleColor() {
return this.appTheme == "light" return this.appTheme == "light" ? "rgba(134,142,150,0.2)" : "rgba(206,212,218,0.1)"
? "rgba(134,142,150,0.2)"
: "rgba(206,212,218,0.1)"
}, },
backgroundColor() { backgroundColor() {
return this.appTheme == "light" ? "#fff" : "#343a40" return this.appTheme == "light" ? "#fff" : "#343a40"

206
app/i18n/en.default.json Normal file
View file

@ -0,0 +1,206 @@
{
"app.name": "EnRecipes",
"EnRecipes": "EnRecipes",
"Try Later": "Try Later",
"Favourites": "Favourites",
"Meal Planner": "Meal Planner",
"Categories": "Categories",
"DONE": "DONE",
"RENAME": "RENAME",
"Settings": "Settings",
"About": "About",
"Appetizers": "Appetizers",
"Barbecue": "Barbecue",
"Beverages": "Beverages",
"Breads": "Breads",
"Breakfast": "Breakfast",
"Desserts": "Desserts",
"Dinner": "Dinner",
"Drinks": "Drinks",
"Healthy": "Healthy",
"Lunch": "Lunch",
"Main dishes": "Main dishes",
"Meat": "Meat",
"Noodles": "Noodles",
"Pasta": "Pasta",
"Poultry": "Poultry",
"Rice": "Rice",
"Salads": "Salads",
"Sauces": "Sauces",
"Seafood": "Seafood",
"Side dishes": "Side dishes",
"Snacks": "Snacks",
"Soups": "Soups",
"Undefined": "Undefined",
"Vegan": "Vegan",
"Vegetarian": "Vegetarian",
"Serving": "Serving",
"Piece": "Piece",
"Teaspoon": "Teaspoon",
"Tablespoon": "Tablespoon",
"Fluid Ounce": "Fluid Ounce",
"Ounce": "Ounce",
"Pound": "Pound",
"Gram": "Gram",
"Kilogram": "Kilogram",
"Cup": "Cup",
"Gallon": "Gallon",
"Millilitre": "Millilitre",
"Litre": "Litre",
"Roll": "Roll",
"Patty": "Patty",
"Loaf": "Loaf",
"unit": "unit",
"Unit": "Unit",
"tsp": "tsp",
"dsp": "dsp",
"tbsp": "tbsp",
"fl oz": "fl oz",
"cup": "cup",
"pt": "pt",
"qt": "qt",
"gal": "gal",
"ml": "ml",
"l": "l",
"oz": "oz",
"lb": "lb",
"mg": "mg",
"g": "g",
"kg": "kg",
"cm": "cm",
"in": "in",
"leaf": "leaf",
"clove": "clove",
"piece": "piece",
"pinch": "pinch",
"drop": "drop",
"dozen": "dozen",
"stick": "stick",
"small": "small",
"medium": "medium",
"large": "large",
"Start adding your recipes!": "Start adding your recipes!",
"Use the plus button to add one": "Use the plus button to add one",
"Use the pencil button to add some ingredients": "Use the pencil button to add some ingredients",
"Use the pencil button to add some instructions": "Use the pencil button to add some instructions",
"Use the pencil button to add some combinations": "Use the pencil button to add some combinations",
"Use the pencil button to add some notes": "Use the pencil button to add some notes",
"All done!": "All done!",
"Recipes you mark as try later will be listed here": "Recipes you mark as try later will be listed here",
"No favourites yet": "No favourites yet",
"Recipes you mark as favourite will be listed here": "Recipes you mark as favourite will be listed here",
"Category looks empty": "Category looks empty",
"No recipes found": "No recipes found",
"Your search did not match any recipes": "Your search did not match any recipes",
"Your search did not match any recipes in this category": "Your search did not match any recipes in this category",
"Your search did not match any recipes in your favourites": "Your search did not match any recipes in your favourites",
"Your search did not match any recipes in your try later list": "Your search did not match any recipes in your try later list",
"Interface": "Interface",
"App language": "App language",
"System default": "System default",
"Theme": "Theme",
"Light": "Light",
"Dark": "Dark",
"Database": "Database",
"Export a full backup": "Export a full backup",
"Generates a zip file that contains all your data. This file can be imported back.": "Generates a zip file that contains all your data. This file can be imported back.",
"Import from backup": "Import from backup",
"Supports full backups exported by this app": "Supports full backups exported by this app",
"Version": "Version",
"View project on GitHub": "View project on GitHub",
"Join the Telegram group": "Join the Telegram group",
"for reporting issues, suggestions and feedback": "for reporting issues, suggestions and feedback",
"Author": "Author",
"Vishnu Raghav B": "Vishnu Raghav B",
"Follow on GitHub": "Follow on GitHub",
"Follow on Mastodon": "Follow on Mastodon",
"New recipe": "New recipe",
"Edit recipe": "Edit recipe",
"Title": "Title",
"Category": "Category",
"Preparation time": "Preparation time",
"Cooking time": "Cooking time",
"Yield quantity": "Yield quantity",
"Yield measured in": "Yield measured in",
"Ingredient": "Ingredient",
"Ingredients": "Ingredients",
"ADD INGREDIENT": "ADD INGREDIENT",
"Instruction": "Instruction",
"Instructions": "Instructions",
"ADD STEP": "ADD STEP",
"Combinations": "Combinations",
"ADD COMBINATION": "ADD COMBINATION",
"Note": "Note",
"Notes": "Notes",
"ADD NOTE": "ADD NOTE",
"Item": "Item",
"Step": "Step",
"Note": "Note",
"Sort by": "Sort by",
"Natural order": "Natural order",
"Duration": "Duration",
"Last modified": "Last modified",
"CANCEL": "CANCEL",
"REMOVE": "REMOVE",
"ADD": "ADD",
"Select a recipe": "Select a recipe",
"Nothing here! Add some recipes and try again.": "Nothing here! Add some recipes and try again.",
"My Healthy Recipe": "My Healthy Recipe",
"hr": "hr",
"min": "min",
"Photo": "Photo",
"Recipe": "Recipe",
"Overview": "Overview",
"Share": "Share",
"ADD NEW": "ADD NEW",
"SET": "SET",
"Restart required": "Restart required",
"RESTART": "RESTART",
"EnRecipes needs to be restarted for the theme change to take effect.": "EnRecipes needs to be restarted for the theme change to take effect.",
"EnRecipes needs to be restarted for the app language to take effect.": "EnRecipes needs to be restarted for the app language to take effect.",
"Grant permission": "Grant permission",
"EnRecipes requires storage permission in order to import your data from a previous backup.": "EnRecipes requires storage permission in order to import your data from a previous backup.",
"NOT NOW": "NOT NOW",
"CONTINUE": "CONTINUE",
"EnRecipes requires storage permission in order to backup your data to this device.": "EnRecipes requires storage permission in order to backup your data to this device.",
"Add at least one recipe to perform a backup": "Add at least one recipe to perform a backup",
"Added to Favourites": "Added to Favourites",
"Removed from Favourites": "Removed from Favourites",
"Added to Try Later": "Added to Try Later",
"Removed from Try Later": "Removed from Try Later",
"Delete recipe?": "Delete recipe?",
"DELETE": "DELETE",
"Are you sure you want to delete the recipe": "Are you sure you want to delete the recipe",
"Search": "Search",
"Rename category": "Rename category",
"Unsaved changes": "Unsaved changes",
"Are you sure you want to discard unsaved changes to this recipe?": "Are you sure you want to discard unsaved changes to this recipe?",
"DISCARD": "DISCARD",
"KEEP EDITING": "KEEP EDITING",
"Remove ingredient?": "Remove ingredient?",
"Remove instruction?": "Remove instruction?",
"Remove combination?": "Remove combination?",
"Remove note?": "Remove note?",
"New yield unit": "New yield unit",
"New category": "New category",
"Required": "Required",
"EnRecipes requires storage permission in order to set recipe photo.": "EnRecipes requires storage permission in order to set recipe photo.",
"Recipe photo": "Recipe photo",
"REPLACE PHOTO": "REPLACE PHOTO",
"Crop photo": "Crop photo",
"breakfast": "breakfast",
"lunch": "lunch",
"dinner": "dinner",
"snacks": "snacks",
"You tried this recipe:": "You tried this recipe:",
"today": "today",
"yesterday": "yesterday",
"days ago": "days ago",
"weeks ago": "weeks ago",
"months ago": "months ago",
"long time ago": "long time ago",
"Verifying...": "Verifying...",
"Import successful": "Import successful",
"Permission denied": "Permission denied",
"Shared via EnRecipes. Get it on Play Store or F-Droid.": "Shared via EnRecipes. Get it on Play Store or F-Droid."
}

206
app/i18n/ta.json Normal file
View file

@ -0,0 +1,206 @@
{
"app.name": "என்ரெசிபீஸ்",
"EnRecipes": "என்ரெசிபீஸ்",
"Try Later": "பின்னர் முயற்சிக்க",
"Favourites": "பிடித்தவை",
"Meal Planner": "உணவுத் திட்டம்",
"Categories": "வகைகள்",
"DONE": "முடிந்தது",
"RENAME": "மறுபெயரிடு",
"Settings": "அமைப்புகள்",
"About": "பற்றி",
"Appetizers": "பசித்தூண்டி",
"Barbecue": "பார்பிக்யூ",
"Beverages": "பானங்கள்",
"Breads": "ரொட்டிகள்",
"Breakfast": "காலை உணவு",
"Desserts": "இனிப்புகள்",
"Dinner": "இரவு உணவு",
"Drinks": "மதுபானம்",
"Healthy": "ஆரோக்கியமானவை",
"Lunch": "மதிய உணவு",
"Main dishes": "முக்கிய உணவுகள்",
"Meat": "இறைச்சி",
"Noodles": "நூடுல்ஸ்",
"Pasta": "பாஸ்தா",
"Poultry": "கோழி",
"Rice": "சோறு",
"Salads": "சாலடுகள்",
"Sauces": "சாஸ்கள்",
"Seafood": "கடல் உணவு",
"Side dishes": "தொடு கறிகள்",
"Snacks": "தின்பண்டங்கள்",
"Soups": "சூப்கள்",
"Undefined": "வரையறுக்கப்படாதவை",
"Vegan": "வேகன்",
"Vegetarian": "சைவம்",
"Serving": "கூறு",
"Piece": "துண்டு",
"Teaspoon": "டீஸ்பூன்",
"Tablespoon": "தேக்கரண்டி",
"Fluid Ounce": "திரவ அவுன்ஸ்",
"Ounce": "அவுன்ஸ்",
"Pound": "பவுண்டு",
"Gram": "கிராம்",
"Kilogram": "கிலோகிராம்",
"Cup": "கோப்பை",
"Gallon": "கேலன்",
"Millilitre": "மில்லிலிட்டர்",
"Litre": "லிட்டர்",
"Roll": "ரோல்",
"Patty": "வடை",
"Loaf": "ரொட்டித் துண்டு",
"unit": "அலகு",
"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": "பெரிய",
"Start adding your recipes!": "உங்கள் சமையல் குறிப்புகளைச் சேர்க்கத் தொடங்குங்கள்!",
"Use the plus button to add one": "ஒரு செய்முறையைச் சேர்க்க பிளஸ் பொத்தானைப் பயன்படுத்தவும்",
"Use the pencil button to add some ingredients": "தேவையான பொருட்களைச் சேர்க்க பென்சில் பொத்தானைப் பயன்படுத்தவும்",
"Use the pencil button to add some instructions": "செய்முறைகளைச் சேர்க்க பென்சில் பொத்தானைப் பயன்படுத்தவும்",
"Use the pencil button to add some combinations": "சில சேர்க்கைகளைச் சேர்க்க பென்சில் பொத்தானைப் பயன்படுத்தவும்",
"Use the pencil button to add some notes": "சில குறிப்புகளைச் சேர்க்க பென்சில் பொத்தானைப் பயன்படுத்தவும்",
"All done!": "அனைத்தும் செய்யப்பட்டுள்ளன!",
"Recipes you mark as try later will be listed here": "முயற்சிக்க வேண்டும் என நீங்கள் குறிக்கும் சமையல் குறிப்புகள் இங்கே பட்டியலிடப்படும்",
"No favourites yet": "இன்னும் பிடித்தவை எதுவும் இல்லை",
"Recipes you mark as favourite will be listed here": "நீங்கள் பிடித்ததாகக் குறிக்கும் சமையல் குறிப்புகள் இங்கே பட்டியலிடப்படும்",
"Category looks empty": "இந்த பிரிவில் எந்த சமையல் குறிப்புகளும் இல்லை",
"No recipes found": "எந்த சமையல் குறிப்புகளும் இல்லை",
"Your search did not match any recipes": "உங்கள் தேடல் எந்த சமையல் குறிப்புகளுக்கும் பொருந்தவில்லை",
"Your search did not match any recipes in this category": "உங்கள் தேடல் இந்த வகையில் எந்த சமையல் குறிப்புகளுடன் பொருந்தவில்லை",
"Your search did not match any recipes in your favourites": "உங்கள் தேடல் உங்களுக்கு பிடித்தவற்றில் எந்த சமையல் குறிப்புகளுடன் பொருந்தவில்லைை",
"Your search did not match any recipes in your try later list": "உங்கள் தேடல் நீங்கள் முயற்சிக்க வேண்டிய பட்டியலில் உள்ள எந்த சமையல் குறிப்புகளுடன் பொருந்தவில்லைை",
"Interface": "இடைமுகம்",
"App language": "பயன்பாட்டு மொழி",
"System default": "கணினி இயல்புநிலை",
"Theme": "தீம்",
"Light": "வெளிச்சம்",
"Dark": "இருள்",
"Database": "தரவுத்தளம்",
"Export a full backup": "முழு காப்புப்பிரதியை ஏற்றுமதி செய்க",
"Generates a zip file that contains all your data. This file can be imported back.": "இது உங்கள் எல்லா தரவையும் கொண்ட ஒரு ஜிப் கோப்பை உருவாக்குகிறது. இந்த கோப்பை மீண்டும் இறக்குமதி செய்யலாம்.",
"Import from backup": "காப்புப்பிரதியிலிருந்து இறக்குமதி செய்க",
"Supports full backups exported by this app": "இந்த பயன்பாட்டின் மூலம் ஏற்றுமதி செய்யப்பட்ட முழு காப்புப்பிரதிகளை ஆதரிக்கும்",
"Version": "பதிப்பு",
"View project on GitHub": "கிட்ஹப்பில் திட்டத்தைக் காண்க",
"Join the Telegram group": "டெலிகிராம் குழுவில் சேரவும்",
"for reporting issues, suggestions and feedback": "சிக்கல்கள், பரிந்துரைகள் மற்றும் கருத்துக்களைப் புகாரளிக்க",
"Author": "பயன்பாட்டு ஆசிரியர்",
"Vishnu Raghav B": "விஷ்ணு ராகவ் .பா",
"Follow on GitHub": "கிட்ஹப்பில் அவரைப் பின்தொடரவும்",
"Follow on Mastodon": "மஸ்டோடனில் அவரைப் பின்தொடரவும்",
"New recipe": "புதிய செய்முறை",
"Edit recipe": "செய்முறையைத் திருத்து",
"Title": "தலைப்பு",
"Category": "வகை",
"Preparation time": "தயாரிப்பு நேரம்",
"Cooking time": "சமைக்கும் நேரம்",
"Yield quantity": "மகசூல் அளவு",
"Yield measured in": "மகசூல் அலகு",
"Ingredient": "தேவையான பொருள்",
"Ingredients": "தேவையான பொருட்கள்",
"ADD INGREDIENT": "பொருளைச் சேர்",
"Instruction": "செய்முறை",
"Instructions": "செய்முறைகள்",
"ADD STEP": "செய்முறையைச் சேர்",
"Combinations": "சேர்க்கைகள்",
"ADD COMBINATION": "சேர்க்கையைச் சேர்",
"Note": "குறிப்பு",
"Notes": "குறிப்புகள்",
"ADD NOTE": "குறிப்பைச் சேர்",
"Item": "பொருள்",
"Step": "செயஂமுறை",
"Note": "குறிப்பு",
"Sort by": "வரிசை விதி",
"Natural order": "இயற்கை வரிசை",
"Duration": "நேரம்",
"Last modified": "கடைசியாக மாற்றப்பட்டது",
"CANCEL": "ரத்துசெய்",
"REMOVE": "அகற்று",
"ADD": "சேர்",
"Select a recipe": "ஒரு செய்முறையைத் தேர்ந்தெடுக்கவும்",
"Nothing here! Add some recipes and try again.": "இங்கு எதுவுமில்லை! சில சமையல் வகைகளைச் சேர்த்து மீண்டும் முயற்சிக்கவும்.",
"My Healthy Recipe": "எனது ஆரோக்கியமான செய்முறை",
"hr": "மணி",
"min": "நிமிடம்",
"Photo": "புகைப்படம்",
"Recipe": "செய்முறை",
"Overview": "கண்ணோட்டம்",
"Share": "பகிர்",
"ADD NEW": "புதியனவற்றை சேர்",
"SET": "அமை",
"Restart required": "மறுதொடக்கம் தேவை",
"RESTART": "மறுதொடக்கம்",
"EnRecipes needs to be restarted for the theme change to take effect.": "தீம் மாற்றம் நடைமுறைக்கு வருவதற்கு என்ரெசிபீஸ் ஐ மீண்டும் தொடங்க வேண்டும்.",
"EnRecipes needs to be restarted for the app language to take effect.": "பயன்பாட்டு மொழி நடைமுறைக்கு வருவதற்கு என்ரெசிபீஸ் ஐ மீண்டும் தொடங்க வேண்டும்.",
"Grant permission": "அனுமதி வழங்கவும்",
"EnRecipes requires storage permission in order to import your data from a previous backup.": "முந்தைய காப்புப்பிரதியிலிருந்து உங்கள் தரவை இறக்குமதி செய்ய என்ரெசிபீஸ்க்கு சேமிப்பு அனுமதி தேவைப்படுகிறது.",
"NOT NOW": "இப்போது இல்லை",
"CONTINUE": "தொடர்",
"EnRecipes requires storage permission in order to backup your data to this device.": "இந்தச் சாதனத்தில் உங்கள் தரவை காப்புப் பிரதி எடுக்க என்ரெசிபீஸ்க்கு சேமிப்பக அனுமதி தேவைப்படுகிறது.",
"Add at least one recipe to perform a backup": "காப்புப்பிரதி செய்ய குறைந்தபட்சம் ஒரு செய்முறையாவது சேர்க்கவும்",
"Added to Favourites": "பிடித்தவையில் சேர்க்கப்பட்டது",
"Removed from Favourites": "பிடித்தவற்றிலிருந்து அகற்றப்பட்டது",
"Added to Try Later": "பின்னர் முயற்சிக்க சேர்க்கப்பட்டது",
"Removed from Try Later": "பின்னர் முயற்சிப்பதில் இருந்து நீக்கப்பட்டது",
"Delete recipe?": "செய்முறையை நீக்கவா?",
"DELETE": "நீக்கு",
"Are you sure you want to delete the recipe": "இந்த செய்முறையை நீக்க விரும்புகிறீர்களா",
"Search": "தேடு",
"Rename category": "வகையை மறுபெயரிடுங்கள்",
"Unsaved changes": "சேமிக்கப்படாத மாற்றங்கள்",
"Are you sure you want to discard unsaved changes to this recipe?": "இந்த செய்முறையில் சேமிக்கப்படாத மாற்றங்களை நிராகரிக்க விரும்புகிறீர்களா?",
"DISCARD": "நிராகரி",
"KEEP EDITING": "தொடர்ந்து திருத்து",
"Remove ingredient?": "பொருளை அகற்றவா?",
"Remove instruction?": "செய்முறையை அகற்றவா?",
"Remove combination?": "சேர்க்கையை அகற்றவா?",
"Remove note?": "குறிப்பை அகற்றவா?",
"New yield unit": "புதிய மகசூல் அலகு",
"New category": "புதிய வகை",
"Required": "தேவையான",
"EnRecipes requires storage permission in order to set recipe photo.": "செய்முறை புகைப்படத்தை அமைக்க என்ரெசிபீஸ்க்கு சேமிப்பு அனுமதி தேவை.",
"Recipe photo": "செய்முறை புகைப்படம்",
"REPLACE PHOTO": "புகைப்படத்தை மாற்றவும்",
"Crop photo": "படத்தை வெட்டு",
"breakfast": "காலை உணவு",
"lunch": "மதிய உணவு",
"dinner": "இரவு உணவு",
"snacks": "சிற்றிடை உணவு",
"You tried this recipe:": "இந்த செய்முறையை முயற்சித்தீர்கள்:",
"today": "இன்று",
"yesterday": "நேற்று",
"days ago": "நாட்களுக்கு முன்பு",
"weeks ago": "வாரங்களுக்கு முன்பு",
"months ago": "மாதங்களுக்கு முன்பு",
"long time ago": "வெகுகாலத்திற்கு முன்பு",
"Verifying...": "சரிபார்க்கிறது...",
"Import successful": "வெற்றிகரமாக இறக்குமதி செய்யப்பட்டது",
"Permission denied": "அனுமதி மறுக்கப்பட்டது",
"Shared via EnRecipes. Get it on Play Store or F-Droid.": "என்ரெசிபீஸ் வழியாக பகிரப்பட்டது. அதை ப்ளே ஸ்டோர் அல்லது எஃப்-டிரயோடு இருந்து பெறுங்கள்."
}

View file

@ -1,3 +1,16 @@
import {
localize,
androidLaunchEventLocalizationHandler
} from '@nativescript/localize'
import {
on,
launchEvent
} from '@nativescript/core/application';
on(launchEvent, (args) => {
if (args.android) {
androidLaunchEventLocalizationHandler();
}
})
import Vue from "nativescript-vue" import Vue from "nativescript-vue"
import App from "./components/App" import App from "./components/App"
import store from "./store" import store from "./store"
@ -26,7 +39,9 @@ Vue.use(CalendarView)
import RadSideDrawer from "nativescript-ui-sidedrawer/vue" import RadSideDrawer from "nativescript-ui-sidedrawer/vue"
Vue.use(RadSideDrawer) Vue.use(RadSideDrawer)
import { CheckBox } from "@nstudio/nativescript-checkbox" import {
CheckBox
} from "@nstudio/nativescript-checkbox"
Vue.registerElement("CheckBox", () => CheckBox, { Vue.registerElement("CheckBox", () => CheckBox, {
model: { model: {
prop: "checked", prop: "checked",
@ -35,6 +50,7 @@ Vue.registerElement("CheckBox", () => CheckBox, {
}) })
Vue.config.silent = TNS_ENV === "production" Vue.config.silent = TNS_ENV === "production"
Vue.filter('L', localize)
new Vue({ new Vue({
store, store,

View file

@ -15,17 +15,22 @@
android { android {
defaultConfig { defaultConfig {
versionCode 2 versionCode 3
versionName '1.0.1' versionName '1.1.2'
applicationId 'com.vishnuraghav.enrecipes' applicationId 'com.vishnuraghav.enrecipes'
minSdkVersion 21 minSdkVersion 21
generatedDensities = [] generatedDensities = []
// ndk { ndk {
// abiFilters.clear() abiFilters.clear()
// abiFilters.addAll(['x86','armeabi-v7a','arm64-v8a']) abiFilters.addAll(['x86','armeabi-v7a','arm64-v8a'])
// } }
} }
aaptOptions { aaptOptions {
additionalParameters "--no-version-vectors" additionalParameters "--no-version-vectors"
} }
// bundle {
// language {
// enableSplit = false
// }
// }
} }

View file

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View file

Before

Width:  |  Height:  |  Size: 687 B

After

Width:  |  Height:  |  Size: 687 B

View file

Before

Width:  |  Height:  |  Size: 567 B

After

Width:  |  Height:  |  Size: 567 B

View file

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 339 B

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

Before

Width:  |  Height:  |  Size: 517 B

After

Width:  |  Height:  |  Size: 517 B

View file

Before

Width:  |  Height:  |  Size: 197 B

After

Width:  |  Height:  |  Size: 197 B

View file

Before

Width:  |  Height:  |  Size: 488 B

After

Width:  |  Height:  |  Size: 488 B

View file

Before

Width:  |  Height:  |  Size: 390 B

After

Width:  |  Height:  |  Size: 390 B

View file

Before

Width:  |  Height:  |  Size: 336 B

After

Width:  |  Height:  |  Size: 336 B

View file

Before

Width:  |  Height:  |  Size: 229 B

After

Width:  |  Height:  |  Size: 229 B

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 302 B

After

Width:  |  Height:  |  Size: 302 B

View file

Before

Width:  |  Height:  |  Size: 173 B

After

Width:  |  Height:  |  Size: 173 B

View file

Before

Width:  |  Height:  |  Size: 303 B

After

Width:  |  Height:  |  Size: 303 B

View file

Before

Width:  |  Height:  |  Size: 345 B

After

Width:  |  Height:  |  Size: 345 B

View file

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 324 B

View file

Before

Width:  |  Height:  |  Size: 198 B

After

Width:  |  Height:  |  Size: 198 B

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 300 B

View file

Before

Width:  |  Height:  |  Size: 161 B

After

Width:  |  Height:  |  Size: 161 B

View file

Before

Width:  |  Height:  |  Size: 363 B

After

Width:  |  Height:  |  Size: 363 B

View file

Before

Width:  |  Height:  |  Size: 626 B

After

Width:  |  Height:  |  Size: 626 B

View file

Before

Width:  |  Height:  |  Size: 547 B

After

Width:  |  Height:  |  Size: 547 B

View file

Before

Width:  |  Height:  |  Size: 266 B

After

Width:  |  Height:  |  Size: 266 B

View file

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

Before

Width:  |  Height:  |  Size: 437 B

After

Width:  |  Height:  |  Size: 437 B

View file

Before

Width:  |  Height:  |  Size: 165 B

After

Width:  |  Height:  |  Size: 165 B

View file

Before

Width:  |  Height:  |  Size: 552 B

After

Width:  |  Height:  |  Size: 552 B

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 969 B

After

Width:  |  Height:  |  Size: 969 B

View file

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 434 B

View file

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

Before

Width:  |  Height:  |  Size: 797 B

After

Width:  |  Height:  |  Size: 797 B

View file

Before

Width:  |  Height:  |  Size: 165 B

After

Width:  |  Height:  |  Size: 165 B

View file

Before

Width:  |  Height:  |  Size: 979 B

After

Width:  |  Height:  |  Size: 979 B

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 818 B

After

Width:  |  Height:  |  Size: 818 B

View file

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 449 B

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

Before

Width:  |  Height:  |  Size: 697 B

After

Width:  |  Height:  |  Size: 697 B

View file

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

View file

Before

Width:  |  Height:  |  Size: 1,020 B

After

Width:  |  Height:  |  Size: 1,020 B

View file

@ -0,0 +1,208 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="_Shared_via_EnRecipes__Get_it_on_Play_Store_or_F_Droid__Z2oTOK1">"என்ரெசிபீஸ் வழியாக பகிரப்பட்டது. அதை ப்ளே ஸ்டோர் அல்லது எஃப்-டிரயோடு இருந்து பெறுங்கள்."</string>
<string name="_Permission_denied_Z7wlWK">"அனுமதி மறுக்கப்பட்டது"</string>
<string name="_Import_successful_2bLM3e">"வெற்றிகரமாக இறக்குமதி செய்யப்பட்டது"</string>
<string name="_Verifying____cS102">"சரிபார்க்கிறது..."</string>
<string name="_long_time_ago_1nwlFA">"வெகுகாலத்திற்கு முன்பு"</string>
<string name="_months_ago_Z1TsskA">"மாதங்களுக்கு முன்பு"</string>
<string name="_weeks_ago_Z2sT1GA">"வாரங்களுக்கு முன்பு"</string>
<string name="_days_ago_2iCYkm">"நாட்களுக்கு முன்பு"</string>
<string name="yesterday">"நேற்று"</string>
<string name="today">"இன்று"</string>
<string name="_You_tried_this_recipe__ZUJOl3">"இந்த செய்முறையை முயற்சித்தீர்கள்:"</string>
<string name="snacks">"சிற்றிடை உணவு"</string>
<string name="dinner">"இரவு உணவு"</string>
<string name="lunch">"மதிய உணவு"</string>
<string name="breakfast">"காலை உணவு"</string>
<string name="_Crop_photo_1VL04J">"படத்தை வெட்டு"</string>
<string name="_REPLACE_PHOTO_ZLHhwH">"புகைப்படத்தை மாற்றவும்"</string>
<string name="_Recipe_photo_eqm9B">"செய்முறை புகைப்படம்"</string>
<string name="_EnRecipes_requires_storage_permission_in_order_to_set_recipe_photo__23OnNT">"செய்முறை புகைப்படத்தை அமைக்க என்ரெசிபீஸ்க்கு சேமிப்பு அனுமதி தேவை."</string>
<string name="Required">"தேவையான"</string>
<string name="_New_category_Weg73">"புதிய வகை"</string>
<string name="_New_yield_unit_1Oevd2">"புதிய மகசூல் அலகு"</string>
<string name="_Remove_note__2wyMVE">"குறிப்பை அகற்றவா?"</string>
<string name="_Remove_combination__1dr2LS">"சேர்க்கையை அகற்றவா?"</string>
<string name="_Remove_instruction__vkJfi">"செய்முறையை அகற்றவா?"</string>
<string name="_Remove_ingredient__2puvMO">"பொருளை அகற்றவா?"</string>
<string name="_KEEP_EDITING_1o9uy3">"தொடர்ந்து திருத்து"</string>
<string name="DISCARD">"நிராகரி"</string>
<string name="_Are_you_sure_you_want_to_discard_unsaved_changes_to_this_recipe__Z1G7sVC">"இந்த செய்முறையில் சேமிக்கப்படாத மாற்றங்களை நிராகரிக்க விரும்புகிறீர்களா?"</string>
<string name="_Unsaved_changes_9fqb4">"சேமிக்கப்படாத மாற்றங்கள்"</string>
<string name="_Rename_category_Z1WzCco">"வகையை மறுபெயரிடுங்கள்"</string>
<string name="Search">"தேடு"</string>
<string name="_Are_you_sure_you_want_to_delete_the_recipe_29DH9w">"இந்த செய்முறையை நீக்க விரும்புகிறீர்களா"</string>
<string name="DELETE">"நீக்கு"</string>
<string name="_Delete_recipe__Z2tMYtE">"செய்முறையை நீக்கவா?"</string>
<string name="_Removed_from_Try_Later_1h5Bkp">"பின்னர் முயற்சிப்பதில் இருந்து நீக்கப்பட்டது"</string>
<string name="_Added_to_Try_Later_Z1gaF37">"பின்னர் முயற்சிக்க சேர்க்கப்பட்டது"</string>
<string name="_Removed_from_Favourites_Z5k7LR">"பிடித்தவற்றிலிருந்து அகற்றப்பட்டது"</string>
<string name="_Added_to_Favourites_2mzllO">"பிடித்தவையில் சேர்க்கப்பட்டது"</string>
<string name="_Add_at_least_one_recipe_to_perform_a_backup_17xYaH">"காப்புப்பிரதி செய்ய குறைந்தபட்சம் ஒரு செய்முறையாவது சேர்க்கவும்"</string>
<string name="_EnRecipes_requires_storage_permission_in_order_to_backup_your_data_to_this_device__2cbvIq">"இந்தச் சாதனத்தில் உங்கள் தரவை காப்புப் பிரதி எடுக்க என்ரெசிபீஸ்க்கு சேமிப்பக அனுமதி தேவைப்படுகிறது."</string>
<string name="CONTINUE">"தொடர்"</string>
<string name="_NOT_NOW_Z1HD7qX">"இப்போது இல்லை"</string>
<string name="_EnRecipes_requires_storage_permission_in_order_to_import_your_data_from_a_previous_backup__1CKjb7">"முந்தைய காப்புப்பிரதியிலிருந்து உங்கள் தரவை இறக்குமதி செய்ய என்ரெசிபீஸ்க்கு சேமிப்பு அனுமதி தேவைப்படுகிறது."</string>
<string name="_Grant_permission_Z1UQBuh">"அனுமதி வழங்கவும்"</string>
<string name="_EnRecipes_needs_to_be_restarted_for_the_app_language_to_take_effect__ZWGUtY">"பயன்பாட்டு மொழி நடைமுறைக்கு வருவதற்கு என்ரெசிபீஸ் ஐ மீண்டும் தொடங்க வேண்டும்."</string>
<string name="_EnRecipes_needs_to_be_restarted_for_the_theme_change_to_take_effect__2kU319">"தீம் மாற்றம் நடைமுறைக்கு வருவதற்கு என்ரெசிபீஸ் ஐ மீண்டும் தொடங்க வேண்டும்."</string>
<string name="RESTART">"மறுதொடக்கம்"</string>
<string name="_Restart_required_Z22YINL">"மறுதொடக்கம் தேவை"</string>
<string name="SET">"அமை"</string>
<string name="_ADD_NEW_Zv8FjM">"புதியனவற்றை சேர்"</string>
<string name="Share">"பகிர்"</string>
<string name="Overview">"கண்ணோட்டம்"</string>
<string name="Recipe">"செய்முறை"</string>
<string name="Photo">"புகைப்படம்"</string>
<string name="min">"நிமிடம்"</string>
<string name="hr">"மணி"</string>
<string name="_My_Healthy_Recipe_Z1EqB8f">"எனது ஆரோக்கியமான செய்முறை"</string>
<string name="_Nothing_here__Add_some_recipes_and_try_again__Z25A7Q">"இங்கு எதுவுமில்லை! சில சமையல் வகைகளைச் சேர்த்து மீண்டும் முயற்சிக்கவும்."</string>
<string name="_Select_a_recipe_2lL8x2">"ஒரு செய்முறையைத் தேர்ந்தெடுக்கவும்"</string>
<string name="ADD">"சேர்"</string>
<string name="REMOVE">"அகற்று"</string>
<string name="CANCEL">"ரத்துசெய்"</string>
<string name="_Last_modified_Z1yBAS3">"கடைசியாக மாற்றப்பட்டது"</string>
<string name="Duration">"நேரம்"</string>
<string name="_Natural_order_Z1O8On5">"இயற்கை வரிசை"</string>
<string name="_Sort_by_Zq01TF">"வரிசை விதி"</string>
<string name="Step">"செயஂமுறை"</string>
<string name="Item">"பொருள்"</string>
<string name="_ADD_NOTE_ZyNw1r">"குறிப்பைச் சேர்"</string>
<string name="Notes">"குறிப்புகள்"</string>
<string name="Note">"குறிப்பு"</string>
<string name="_ADD_COMBINATION_2q9HWq">"சேர்க்கையைச் சேர்"</string>
<string name="Combinations">"சேர்க்கைகள்"</string>
<string name="_ADD_STEP_ZyMPOe">"செய்முறையைச் சேர்"</string>
<string name="Instructions">"செய்முறைகள்"</string>
<string name="Instruction">"செய்முறை"</string>
<string name="_ADD_INGREDIENT_29sSLU">"பொருளைச் சேர்"</string>
<string name="Ingredients">"தேவையான பொருட்கள்"</string>
<string name="Ingredient">"தேவையான பொருள்"</string>
<string name="_Yield_measured_in_2OoRT">"மகசூல் அலகு"</string>
<string name="_Yield_quantity_ZHx3jw">"மகசூல் அளவு"</string>
<string name="_Cooking_time_2w2fEw">"சமைக்கும் நேரம்"</string>
<string name="_Preparation_time_15BfxT">"தயாரிப்பு நேரம்"</string>
<string name="Category">"வகை"</string>
<string name="Title">"தலைப்பு"</string>
<string name="_Edit_recipe_ZPvwDP">"செய்முறையைத் திருத்து"</string>
<string name="_New_recipe_Z1Y2YKJ">"புதிய செய்முறை"</string>
<string name="_Follow_on_Mastodon_Z1v6e4V">"மஸ்டோடனில் அவரைப் பின்தொடரவும்"</string>
<string name="_Follow_on_GitHub_Z1GjptH">"கிட்ஹப்பில் அவரைப் பின்தொடரவும்"</string>
<string name="_Vishnu_Raghav_B_1qnRnC">"விஷ்ணு ராகவ் .பா"</string>
<string name="Author">"பயன்பாட்டு ஆசிரியர்"</string>
<string name="_for_reporting_issues__suggestions_and_feedback_Z1JUmyE">"சிக்கல்கள், பரிந்துரைகள் மற்றும் கருத்துக்களைப் புகாரளிக்க"</string>
<string name="_Join_the_Telegram_group_ZAXQgc">"டெலிகிராம் குழுவில் சேரவும்"</string>
<string name="_View_project_on_GitHub_1hrJ0U">"கிட்ஹப்பில் திட்டத்தைக் காண்க"</string>
<string name="Version">"பதிப்பு"</string>
<string name="_Supports_full_backups_exported_by_this_app_ZYs4f2">"இந்த பயன்பாட்டின் மூலம் ஏற்றுமதி செய்யப்பட்ட முழு காப்புப்பிரதிகளை ஆதரிக்கும்"</string>
<string name="_Import_from_backup_Z28HsC0">"காப்புப்பிரதியிலிருந்து இறக்குமதி செய்க"</string>
<string name="_Generates_a_zip_file_that_contains_all_your_data__This_file_can_be_imported_back__Z19m3xY">"இது உங்கள் எல்லா தரவையும் கொண்ட ஒரு ஜிப் கோப்பை உருவாக்குகிறது. இந்த கோப்பை மீண்டும் இறக்குமதி செய்யலாம்."</string>
<string name="_Export_a_full_backup_TJfra">"முழு காப்புப்பிரதியை ஏற்றுமதி செய்க"</string>
<string name="Database">"தரவுத்தளம்"</string>
<string name="Dark">"இருள்"</string>
<string name="Light">"வெளிச்சம்"</string>
<string name="Theme">"தீம்"</string>
<string name="_System_default_Z1jruVS">"கணினி இயல்புநிலை"</string>
<string name="_App_language_DzFsk">"பயன்பாட்டு மொழி"</string>
<string name="Interface">"இடைமுகம்"</string>
<string name="_Your_search_did_not_match_any_recipes_in_your_try_later_list_Z2wWbA1">"உங்கள் தேடல் நீங்கள் முயற்சிக்க வேண்டிய பட்டியலில் உள்ள எந்த சமையல் குறிப்புகளுடன் பொருந்தவில்லைை"</string>
<string name="_Your_search_did_not_match_any_recipes_in_your_favourites_1ylvHN">"உங்கள் தேடல் உங்களுக்கு பிடித்தவற்றில் எந்த சமையல் குறிப்புகளுடன் பொருந்தவில்லைை"</string>
<string name="_Your_search_did_not_match_any_recipes_in_this_category_P7J4V">"உங்கள் தேடல் இந்த வகையில் எந்த சமையல் குறிப்புகளுடன் பொருந்தவில்லை"</string>
<string name="_Your_search_did_not_match_any_recipes_Z1eppHH">"உங்கள் தேடல் எந்த சமையல் குறிப்புகளுக்கும் பொருந்தவில்லை"</string>
<string name="_No_recipes_found_Z125IxD">"எந்த சமையல் குறிப்புகளும் இல்லை"</string>
<string name="_Category_looks_empty_ZAK5qU">"இந்த பிரிவில் எந்த சமையல் குறிப்புகளும் இல்லை"</string>
<string name="_Recipes_you_mark_as_favourite_will_be_listed_here_Z1iIHgY">"நீங்கள் பிடித்ததாகக் குறிக்கும் சமையல் குறிப்புகள் இங்கே பட்டியலிடப்படும்"</string>
<string name="_No_favourites_yet_aPSoG">"இன்னும் பிடித்தவை எதுவும் இல்லை"</string>
<string name="_Recipes_you_mark_as_try_later_will_be_listed_here_Z1ITwPV">"முயற்சிக்க வேண்டும் என நீங்கள் குறிக்கும் சமையல் குறிப்புகள் இங்கே பட்டியலிடப்படும்"</string>
<string name="_All_done__72KYl">"அனைத்தும் செய்யப்பட்டுள்ளன!"</string>
<string name="_Use_the_pencil_button_to_add_some_notes_Z1wyyeb">"சில குறிப்புகளைச் சேர்க்க பென்சில் பொத்தானைப் பயன்படுத்தவும்"</string>
<string name="_Use_the_pencil_button_to_add_some_combinations_6NhFS">"சில சேர்க்கைகளைச் சேர்க்க பென்சில் பொத்தானைப் பயன்படுத்தவும்"</string>
<string name="_Use_the_pencil_button_to_add_some_instructions_ZAi0PH">"செய்முறைகளைச் சேர்க்க பென்சில் பொத்தானைப் பயன்படுத்தவும்"</string>
<string name="_Use_the_pencil_button_to_add_some_ingredients_Z1xOX8S">"தேவையான பொருட்களைச் சேர்க்க பென்சில் பொத்தானைப் பயன்படுத்தவும்"</string>
<string name="_Use_the_plus_button_to_add_one_1h20ms">"ஒரு செய்முறையைச் சேர்க்க பிளஸ் பொத்தானைப் பயன்படுத்தவும்"</string>
<string name="_Start_adding_your_recipes__rwnVV">"உங்கள் சமையல் குறிப்புகளைச் சேர்க்கத் தொடங்குங்கள்!"</string>
<string name="large">"பெரிய"</string>
<string name="medium">"நடுத்தர"</string>
<string name="small">"சிறிய"</string>
<string name="stick">"குச்சி"</string>
<string name="dozen">"டஜன்"</string>
<string name="drop">"துளி"</string>
<string name="pinch">"கிள்ளு"</string>
<string name="piece">"துண்டு"</string>
<string name="clove">"பல்"</string>
<string name="leaf">"இலை"</string>
<string name="in">"அங்குலம்"</string>
<string name="cm">"சென்டிமீட்டர்"</string>
<string name="kg">"கிலோகிராம்"</string>
<string name="g">"கிராம்"</string>
<string name="mg">"மில்லிகிராம்"</string>
<string name="lb">"பவுண்டு"</string>
<string name="oz">"அவுன்ஸ்"</string>
<string name="l">"லிட்டர்"</string>
<string name="ml">"மில்லிலிட்டர்"</string>
<string name="gal">"கேலன்"</string>
<string name="qt">"குவிண்ட்"</string>
<string name="pt">"பைண்ட்"</string>
<string name="cup">"கோப்பை"</string>
<string name="_fl_oz_72kqu">"திரவ அவுன்ஸ"</string>
<string name="tbsp">"தேக்கரண்டி"</string>
<string name="dsp">"இனிப்பு ஸ்பூன்"</string>
<string name="tsp">"டீஸ்பூன்"</string>
<string name="Unit">"அலகு"</string>
<string name="unit">"அலகு"</string>
<string name="Loaf">"ரொட்டித் துண்டு"</string>
<string name="Patty">"வடை"</string>
<string name="Roll">"ரோல்"</string>
<string name="Litre">"லிட்டர்"</string>
<string name="Millilitre">"மில்லிலிட்டர்"</string>
<string name="Gallon">"கேலன்"</string>
<string name="Cup">"கோப்பை"</string>
<string name="Kilogram">"கிலோகிராம்"</string>
<string name="Gram">"கிராம்"</string>
<string name="Pound">"பவுண்டு"</string>
<string name="Ounce">"அவுன்ஸ்"</string>
<string name="_Fluid_Ounce_bd10L">"திரவ அவுன்ஸ்"</string>
<string name="Tablespoon">"தேக்கரண்டி"</string>
<string name="Teaspoon">"டீஸ்பூன்"</string>
<string name="Piece">"துண்டு"</string>
<string name="Serving">"கூறு"</string>
<string name="Vegetarian">"சைவம்"</string>
<string name="Vegan">"வேகன்"</string>
<string name="Undefined">"வரையறுக்கப்படாதவை"</string>
<string name="Soups">"சூப்கள்"</string>
<string name="Snacks">"தின்பண்டங்கள்"</string>
<string name="_Side_dishes_Z1Et4Vg">"தொடு கறிகள்"</string>
<string name="Seafood">"கடல் உணவு"</string>
<string name="Sauces">"சாஸ்கள்"</string>
<string name="Salads">"சாலடுகள்"</string>
<string name="Rice">"சோறு"</string>
<string name="Poultry">"கோழி"</string>
<string name="Pasta">"பாஸ்தா"</string>
<string name="Noodles">"நூடுல்ஸ்"</string>
<string name="Meat">"இறைச்சி"</string>
<string name="_Main_dishes_u6RGQ">"முக்கிய உணவுகள்"</string>
<string name="Lunch">"மதிய உணவு"</string>
<string name="Healthy">"ஆரோக்கியமானவை"</string>
<string name="Drinks">"மதுபானம்"</string>
<string name="Dinner">"இரவு உணவு"</string>
<string name="Desserts">"இனிப்புகள்"</string>
<string name="Breakfast">"காலை உணவு"</string>
<string name="Breads">"ரொட்டிகள்"</string>
<string name="Beverages">"பானங்கள்"</string>
<string name="Barbecue">"பார்பிக்யூ"</string>
<string name="Appetizers">"பசித்தூண்டி"</string>
<string name="About">"பற்றி"</string>
<string name="Settings">"அமைப்புகள்"</string>
<string name="RENAME">"மறுபெயரிடு"</string>
<string name="DONE">"முடிந்தது"</string>
<string name="Categories">"வகைகள்"</string>
<string name="_Meal_Planner_2aq4oj">"உணவுத் திட்டம்"</string>
<string name="Favourites">"பிடித்தவை"</string>
<string name="_Try_Later_If7PG">"பின்னர் முயற்சிக்க"</string>
<string name="EnRecipes">"என்ரெசிபீஸ்"</string>
<string name="_app_name_1k3Sbz">"என்ரெசிபீஸ்"</string>
<string name="app_name">"என்ரெசிபீஸ்"</string>
<string name="title_activity_kimera">"என்ரெசிபீஸ்"</string>
</resources>

View file

@ -0,0 +1,208 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="_Shared_via_EnRecipes__Get_it_on_Play_Store_or_F_Droid__Z2oTOK1">"Shared via EnRecipes. Get it on Play Store or F-Droid."</string>
<string name="_Permission_denied_Z7wlWK">"Permission denied"</string>
<string name="_Import_successful_2bLM3e">"Import successful"</string>
<string name="_Verifying____cS102">"Verifying..."</string>
<string name="_long_time_ago_1nwlFA">"long time ago"</string>
<string name="_months_ago_Z1TsskA">"months ago"</string>
<string name="_weeks_ago_Z2sT1GA">"weeks ago"</string>
<string name="_days_ago_2iCYkm">"days ago"</string>
<string name="yesterday">"yesterday"</string>
<string name="today">"today"</string>
<string name="_You_tried_this_recipe__ZUJOl3">"You tried this recipe:"</string>
<string name="snacks">"snacks"</string>
<string name="dinner">"dinner"</string>
<string name="lunch">"lunch"</string>
<string name="breakfast">"breakfast"</string>
<string name="_Crop_photo_1VL04J">"Crop photo"</string>
<string name="_REPLACE_PHOTO_ZLHhwH">"REPLACE PHOTO"</string>
<string name="_Recipe_photo_eqm9B">"Recipe photo"</string>
<string name="_EnRecipes_requires_storage_permission_in_order_to_set_recipe_photo__23OnNT">"EnRecipes requires storage permission in order to set recipe photo."</string>
<string name="Required">"Required"</string>
<string name="_New_category_Weg73">"New category"</string>
<string name="_New_yield_unit_1Oevd2">"New yield unit"</string>
<string name="_Remove_note__2wyMVE">"Remove note?"</string>
<string name="_Remove_combination__1dr2LS">"Remove combination?"</string>
<string name="_Remove_instruction__vkJfi">"Remove instruction?"</string>
<string name="_Remove_ingredient__2puvMO">"Remove ingredient?"</string>
<string name="_KEEP_EDITING_1o9uy3">"KEEP EDITING"</string>
<string name="DISCARD">"DISCARD"</string>
<string name="_Are_you_sure_you_want_to_discard_unsaved_changes_to_this_recipe__Z1G7sVC">"Are you sure you want to discard unsaved changes to this recipe?"</string>
<string name="_Unsaved_changes_9fqb4">"Unsaved changes"</string>
<string name="_Rename_category_Z1WzCco">"Rename category"</string>
<string name="Search">"Search"</string>
<string name="_Are_you_sure_you_want_to_delete_the_recipe_29DH9w">"Are you sure you want to delete the recipe"</string>
<string name="DELETE">"DELETE"</string>
<string name="_Delete_recipe__Z2tMYtE">"Delete recipe?"</string>
<string name="_Removed_from_Try_Later_1h5Bkp">"Removed from Try Later"</string>
<string name="_Added_to_Try_Later_Z1gaF37">"Added to Try Later"</string>
<string name="_Removed_from_Favourites_Z5k7LR">"Removed from Favourites"</string>
<string name="_Added_to_Favourites_2mzllO">"Added to Favourites"</string>
<string name="_Add_at_least_one_recipe_to_perform_a_backup_17xYaH">"Add at least one recipe to perform a backup"</string>
<string name="_EnRecipes_requires_storage_permission_in_order_to_backup_your_data_to_this_device__2cbvIq">"EnRecipes requires storage permission in order to backup your data to this device."</string>
<string name="CONTINUE">"CONTINUE"</string>
<string name="_NOT_NOW_Z1HD7qX">"NOT NOW"</string>
<string name="_EnRecipes_requires_storage_permission_in_order_to_import_your_data_from_a_previous_backup__1CKjb7">"EnRecipes requires storage permission in order to import your data from a previous backup."</string>
<string name="_Grant_permission_Z1UQBuh">"Grant permission"</string>
<string name="_EnRecipes_needs_to_be_restarted_for_the_app_language_to_take_effect__ZWGUtY">"EnRecipes needs to be restarted for the app language to take effect."</string>
<string name="_EnRecipes_needs_to_be_restarted_for_the_theme_change_to_take_effect__2kU319">"EnRecipes needs to be restarted for the theme change to take effect."</string>
<string name="RESTART">"RESTART"</string>
<string name="_Restart_required_Z22YINL">"Restart required"</string>
<string name="SET">"SET"</string>
<string name="_ADD_NEW_Zv8FjM">"ADD NEW"</string>
<string name="Share">"Share"</string>
<string name="Overview">"Overview"</string>
<string name="Recipe">"Recipe"</string>
<string name="Photo">"Photo"</string>
<string name="min">"min"</string>
<string name="hr">"hr"</string>
<string name="_My_Healthy_Recipe_Z1EqB8f">"My Healthy Recipe"</string>
<string name="_Nothing_here__Add_some_recipes_and_try_again__Z25A7Q">"Nothing here! Add some recipes and try again."</string>
<string name="_Select_a_recipe_2lL8x2">"Select a recipe"</string>
<string name="ADD">"ADD"</string>
<string name="REMOVE">"REMOVE"</string>
<string name="CANCEL">"CANCEL"</string>
<string name="_Last_modified_Z1yBAS3">"Last modified"</string>
<string name="Duration">"Duration"</string>
<string name="_Natural_order_Z1O8On5">"Natural order"</string>
<string name="_Sort_by_Zq01TF">"Sort by"</string>
<string name="Step">"Step"</string>
<string name="Item">"Item"</string>
<string name="_ADD_NOTE_ZyNw1r">"ADD NOTE"</string>
<string name="Notes">"Notes"</string>
<string name="Note">"Note"</string>
<string name="_ADD_COMBINATION_2q9HWq">"ADD COMBINATION"</string>
<string name="Combinations">"Combinations"</string>
<string name="_ADD_STEP_ZyMPOe">"ADD STEP"</string>
<string name="Instructions">"Instructions"</string>
<string name="Instruction">"Instruction"</string>
<string name="_ADD_INGREDIENT_29sSLU">"ADD INGREDIENT"</string>
<string name="Ingredients">"Ingredients"</string>
<string name="Ingredient">"Ingredient"</string>
<string name="_Yield_measured_in_2OoRT">"Yield measured in"</string>
<string name="_Yield_quantity_ZHx3jw">"Yield quantity"</string>
<string name="_Cooking_time_2w2fEw">"Cooking time"</string>
<string name="_Preparation_time_15BfxT">"Preparation time"</string>
<string name="Category">"Category"</string>
<string name="Title">"Title"</string>
<string name="_Edit_recipe_ZPvwDP">"Edit recipe"</string>
<string name="_New_recipe_Z1Y2YKJ">"New recipe"</string>
<string name="_Follow_on_Mastodon_Z1v6e4V">"Follow on Mastodon"</string>
<string name="_Follow_on_GitHub_Z1GjptH">"Follow on GitHub"</string>
<string name="_Vishnu_Raghav_B_1qnRnC">"Vishnu Raghav B"</string>
<string name="Author">"Author"</string>
<string name="_for_reporting_issues__suggestions_and_feedback_Z1JUmyE">"for reporting issues, suggestions and feedback"</string>
<string name="_Join_the_Telegram_group_ZAXQgc">"Join the Telegram group"</string>
<string name="_View_project_on_GitHub_1hrJ0U">"View project on GitHub"</string>
<string name="Version">"Version"</string>
<string name="_Supports_full_backups_exported_by_this_app_ZYs4f2">"Supports full backups exported by this app"</string>
<string name="_Import_from_backup_Z28HsC0">"Import from backup"</string>
<string name="_Generates_a_zip_file_that_contains_all_your_data__This_file_can_be_imported_back__Z19m3xY">"Generates a zip file that contains all your data. This file can be imported back."</string>
<string name="_Export_a_full_backup_TJfra">"Export a full backup"</string>
<string name="Database">"Database"</string>
<string name="Dark">"Dark"</string>
<string name="Light">"Light"</string>
<string name="Theme">"Theme"</string>
<string name="_System_default_Z1jruVS">"System default"</string>
<string name="_App_language_DzFsk">"App language"</string>
<string name="Interface">"Interface"</string>
<string name="_Your_search_did_not_match_any_recipes_in_your_try_later_list_Z2wWbA1">"Your search did not match any recipes in your try later list"</string>
<string name="_Your_search_did_not_match_any_recipes_in_your_favourites_1ylvHN">"Your search did not match any recipes in your favourites"</string>
<string name="_Your_search_did_not_match_any_recipes_in_this_category_P7J4V">"Your search did not match any recipes in this category"</string>
<string name="_Your_search_did_not_match_any_recipes_Z1eppHH">"Your search did not match any recipes"</string>
<string name="_No_recipes_found_Z125IxD">"No recipes found"</string>
<string name="_Category_looks_empty_ZAK5qU">"Category looks empty"</string>
<string name="_Recipes_you_mark_as_favourite_will_be_listed_here_Z1iIHgY">"Recipes you mark as favourite will be listed here"</string>
<string name="_No_favourites_yet_aPSoG">"No favourites yet"</string>
<string name="_Recipes_you_mark_as_try_later_will_be_listed_here_Z1ITwPV">"Recipes you mark as try later will be listed here"</string>
<string name="_All_done__72KYl">"All done!"</string>
<string name="_Use_the_pencil_button_to_add_some_notes_Z1wyyeb">"Use the pencil button to add some notes"</string>
<string name="_Use_the_pencil_button_to_add_some_combinations_6NhFS">"Use the pencil button to add some combinations"</string>
<string name="_Use_the_pencil_button_to_add_some_instructions_ZAi0PH">"Use the pencil button to add some instructions"</string>
<string name="_Use_the_pencil_button_to_add_some_ingredients_Z1xOX8S">"Use the pencil button to add some ingredients"</string>
<string name="_Use_the_plus_button_to_add_one_1h20ms">"Use the plus button to add one"</string>
<string name="_Start_adding_your_recipes__rwnVV">"Start adding your recipes!"</string>
<string name="large">"large"</string>
<string name="medium">"medium"</string>
<string name="small">"small"</string>
<string name="stick">"stick"</string>
<string name="dozen">"dozen"</string>
<string name="drop">"drop"</string>
<string name="pinch">"pinch"</string>
<string name="piece">"piece"</string>
<string name="clove">"clove"</string>
<string name="leaf">"leaf"</string>
<string name="in">"in"</string>
<string name="cm">"cm"</string>
<string name="kg">"kg"</string>
<string name="g">"g"</string>
<string name="mg">"mg"</string>
<string name="lb">"lb"</string>
<string name="oz">"oz"</string>
<string name="l">"l"</string>
<string name="ml">"ml"</string>
<string name="gal">"gal"</string>
<string name="qt">"qt"</string>
<string name="pt">"pt"</string>
<string name="cup">"cup"</string>
<string name="_fl_oz_72kqu">"fl oz"</string>
<string name="tbsp">"tbsp"</string>
<string name="dsp">"dsp"</string>
<string name="tsp">"tsp"</string>
<string name="Unit">"Unit"</string>
<string name="unit">"unit"</string>
<string name="Loaf">"Loaf"</string>
<string name="Patty">"Patty"</string>
<string name="Roll">"Roll"</string>
<string name="Litre">"Litre"</string>
<string name="Millilitre">"Millilitre"</string>
<string name="Gallon">"Gallon"</string>
<string name="Cup">"Cup"</string>
<string name="Kilogram">"Kilogram"</string>
<string name="Gram">"Gram"</string>
<string name="Pound">"Pound"</string>
<string name="Ounce">"Ounce"</string>
<string name="_Fluid_Ounce_bd10L">"Fluid Ounce"</string>
<string name="Tablespoon">"Tablespoon"</string>
<string name="Teaspoon">"Teaspoon"</string>
<string name="Piece">"Piece"</string>
<string name="Serving">"Serving"</string>
<string name="Vegetarian">"Vegetarian"</string>
<string name="Vegan">"Vegan"</string>
<string name="Undefined">"Undefined"</string>
<string name="Soups">"Soups"</string>
<string name="Snacks">"Snacks"</string>
<string name="_Side_dishes_Z1Et4Vg">"Side dishes"</string>
<string name="Seafood">"Seafood"</string>
<string name="Sauces">"Sauces"</string>
<string name="Salads">"Salads"</string>
<string name="Rice">"Rice"</string>
<string name="Poultry">"Poultry"</string>
<string name="Pasta">"Pasta"</string>
<string name="Noodles">"Noodles"</string>
<string name="Meat">"Meat"</string>
<string name="_Main_dishes_u6RGQ">"Main dishes"</string>
<string name="Lunch">"Lunch"</string>
<string name="Healthy">"Healthy"</string>
<string name="Drinks">"Drinks"</string>
<string name="Dinner">"Dinner"</string>
<string name="Desserts">"Desserts"</string>
<string name="Breakfast">"Breakfast"</string>
<string name="Breads">"Breads"</string>
<string name="Beverages">"Beverages"</string>
<string name="Barbecue">"Barbecue"</string>
<string name="Appetizers">"Appetizers"</string>
<string name="About">"About"</string>
<string name="Settings">"Settings"</string>
<string name="RENAME">"RENAME"</string>
<string name="DONE">"DONE"</string>
<string name="Categories">"Categories"</string>
<string name="_Meal_Planner_2aq4oj">"Meal Planner"</string>
<string name="Favourites">"Favourites"</string>
<string name="_Try_Later_If7PG">"Try Later"</string>
<string name="EnRecipes">"EnRecipes"</string>
<string name="_app_name_1k3Sbz">"EnRecipes"</string>
<string name="app_name">"EnRecipes"</string>
<string name="title_activity_kimera">"EnRecipes"</string>
</resources>

View file

@ -1,7 +1,11 @@
import Vue from "vue" import Vue from "vue"
import Vuex from "vuex" import Vuex from "vuex"
import { Couchbase } from "nativescript-couchbase-plugin" import {
import { getFileAccess } from "@nativescript/core" Couchbase
} from "nativescript-couchbase-plugin"
import {
getFileAccess
} from "@nativescript/core"
const EnRecipesDB = new Couchbase("EnRecipes") const EnRecipesDB = new Couchbase("EnRecipes")
const userCategoriesDB = new Couchbase("userCategories") const userCategoriesDB = new Couchbase("userCategories")
const userYieldUnitsDB = new Couchbase("userYieldUnits") const userYieldUnitsDB = new Couchbase("userYieldUnits")
@ -11,7 +15,7 @@ Vue.use(Vuex)
const defaultCategories = [ const defaultCategories = [
"Appetizers", "Appetizers",
"BBQ", "Barbecue",
"Beverages", "Beverages",
"Breads", "Breads",
"Breakfast", "Breakfast",
@ -81,6 +85,7 @@ export default new Vuex.Store({
"in", "in",
"leaf", "leaf",
"clove", "clove",
"piece",
"pinch", "pinch",
"drop", "drop",
"dozen", "dozen",
@ -135,17 +140,29 @@ export default new Vuex.Store({
outline: "\ueb07", outline: "\ueb07",
calendar: "\uec55", calendar: "\uec55",
today: "\ue97c", today: "\ue97c",
globe: "\uea5a",
}, },
currentComponent: "EnRecipes", currentComponent: "EnRecipes",
language: [{
locale: 'en',
title: 'English'
}, {
locale: 'ta',
title: 'தமிழ்'
}, ]
}, },
mutations: { mutations: {
initializeRecipes(state) { initializeRecipes(state) {
EnRecipesDB.query({ select: [] }).forEach((recipe) => { EnRecipesDB.query({
select: []
}).forEach((recipe) => {
state.recipes.push(recipe) state.recipes.push(recipe)
}) })
}, },
initializeCategories(state) { initializeCategories(state) {
let isCategoriesStored = userCategoriesDB.query({ select: [] }).length let isCategoriesStored = userCategoriesDB.query({
select: []
}).length
if (isCategoriesStored) { if (isCategoriesStored) {
state.userCategories = userCategoriesDB.getDocument( state.userCategories = userCategoriesDB.getDocument(
"userCategories" "userCategories"
@ -155,8 +172,9 @@ export default new Vuex.Store({
categoriesWithRecipes.includes(e) categoriesWithRecipes.includes(e)
) )
} else { } else {
userCategoriesDB.createDocument( userCategoriesDB.createDocument({
{ userCategories: [] }, userCategories: []
},
"userCategories" "userCategories"
) )
} }
@ -164,7 +182,9 @@ export default new Vuex.Store({
state.categories.sort() state.categories.sort()
}, },
initializeYieldUnits(state) { initializeYieldUnits(state) {
let isYieldUnitsStored = userYieldUnitsDB.query({ select: [] }).length let isYieldUnitsStored = userYieldUnitsDB.query({
select: []
}).length
if (isYieldUnitsStored) { if (isYieldUnitsStored) {
state.userYieldUnits = userYieldUnitsDB.getDocument( state.userYieldUnits = userYieldUnitsDB.getDocument(
"userYieldUnits" "userYieldUnits"
@ -174,19 +194,24 @@ export default new Vuex.Store({
yieldUnitsWithRecipes.includes(e) yieldUnitsWithRecipes.includes(e)
) )
} else { } else {
userYieldUnitsDB.createDocument( userYieldUnitsDB.createDocument({
{ userYieldUnits: [] }, userYieldUnits: []
},
"userYieldUnits" "userYieldUnits"
) )
} }
state.yieldUnits = [...defaultYieldUnits, ...state.userYieldUnits] state.yieldUnits = [...defaultYieldUnits, ...state.userYieldUnits]
}, },
initializeMealPlans(state) { initializeMealPlans(state) {
let isMealPlansDBStored = mealPlansDB.query({ select: [] }).length let isMealPlansDBStored = mealPlansDB.query({
select: []
}).length
if (isMealPlansDBStored) { if (isMealPlansDBStored) {
state.mealPlans = mealPlansDB.getDocument("mealPlans").mealPlans state.mealPlans = mealPlansDB.getDocument("mealPlans").mealPlans
} else { } else {
mealPlansDB.createDocument({ mealPlans: [] }, "mealPlans") mealPlansDB.createDocument({
mealPlans: []
}, "mealPlans")
} }
}, },
@ -196,18 +221,23 @@ export default new Vuex.Store({
localRecipesIDs = state.recipes.map((e) => e.id) localRecipesIDs = state.recipes.map((e) => e.id)
partition = recipes.reduce( partition = recipes.reduce(
(result, recipe, i) => { (result, recipe, i) => {
localRecipesIDs.indexOf(recipe.id) < 0 localRecipesIDs.indexOf(recipe.id) < 0 ?
? result[0].push(recipe) // create candidates result[0].push(recipe) // create candidates
: result[1].push(recipe) // update candidates :
result[1].push(recipe) // update candidates
return result return result
}, },
[[], []] [
[],
[]
]
) )
if (partition[0].length) createDocuments(partition[0]) if (partition[0].length) createDocuments(partition[0])
if (partition[1].length) updateDocuments(partition[1]) if (partition[1].length) updateDocuments(partition[1])
} else { } else {
createDocuments(recipes) createDocuments(recipes)
} }
function getUpdatedData(data) { function getUpdatedData(data) {
return data.map((recipe) => { return data.map((recipe) => {
let r = Object.assign({}, recipe) let r = Object.assign({}, recipe)
@ -219,6 +249,7 @@ export default new Vuex.Store({
return r return r
}) })
} }
function createDocuments(data) { function createDocuments(data) {
data = getUpdatedData(data) data = getUpdatedData(data)
state.recipes = [...state.recipes, ...data] state.recipes = [...state.recipes, ...data]
@ -226,6 +257,7 @@ export default new Vuex.Store({
EnRecipesDB.createDocument(recipe, recipe.id) EnRecipesDB.createDocument(recipe, recipe.id)
}) })
} }
function updateDocuments(data) { function updateDocuments(data) {
data = getUpdatedData(data) data = getUpdatedData(data)
data.forEach((recipe) => { data.forEach((recipe) => {
@ -271,7 +303,10 @@ export default new Vuex.Store({
}) })
}, },
addRecipe(state, { id, recipe }) { addRecipe(state, {
id,
recipe
}) {
state.recipes.push(recipe) state.recipes.push(recipe)
EnRecipesDB.createDocument(recipe, id) EnRecipesDB.createDocument(recipe, id)
}, },
@ -296,7 +331,10 @@ export default new Vuex.Store({
state.yieldUnits = [...defaultYieldUnits, ...state.userYieldUnits] state.yieldUnits = [...defaultYieldUnits, ...state.userYieldUnits]
} }
}, },
addMealPlan(state, { event, eventColor }) { addMealPlan(state, {
event,
eventColor
}) {
state.mealPlans.push({ state.mealPlans.push({
title: event.title, title: event.title,
startDate: event.startDate, startDate: event.startDate,
@ -308,7 +346,10 @@ export default new Vuex.Store({
}) })
}, },
deleteRecipe(state, { index, id }) { deleteRecipe(state, {
index,
id
}) {
getFileAccess().deleteFile(state.recipes[index].imageSrc) getFileAccess().deleteFile(state.recipes[index].imageSrc)
state.recipes.splice(index, 1) state.recipes.splice(index, 1)
EnRecipesDB.deleteDocument(id) EnRecipesDB.deleteDocument(id)
@ -319,7 +360,10 @@ export default new Vuex.Store({
} }
}) })
}, },
deleteMealPlan(state, { title, startDate }) { deleteMealPlan(state, {
title,
startDate
}) {
let mealPlan = state.mealPlans.filter((e) => { let mealPlan = state.mealPlans.filter((e) => {
console.log(e.startDate, startDate) console.log(e.startDate, startDate)
let sd = new Date(e.startDate).getTime() let sd = new Date(e.startDate).getTime()
@ -335,14 +379,22 @@ export default new Vuex.Store({
}) })
}, },
overwriteRecipe(state, { id, recipe }) { overwriteRecipe(state, {
id,
recipe
}) {
let index = state.recipes.indexOf( let index = state.recipes.indexOf(
state.recipes.filter((e) => e.id === id)[0] state.recipes.filter((e) => e.id === id)[0]
) )
Object.assign(state.recipes[index], recipe) Object.assign(state.recipes[index], recipe)
EnRecipesDB.updateDocument(id, recipe) EnRecipesDB.updateDocument(id, recipe)
}, },
toggleState(state, { id, recipe, key, setDate }) { toggleState(state, {
id,
recipe,
key,
setDate
}) {
let index = state.recipes.indexOf( let index = state.recipes.indexOf(
state.recipes.filter((e) => e.id === id)[0] state.recipes.filter((e) => e.id === id)[0]
) )
@ -350,7 +402,10 @@ export default new Vuex.Store({
if (setDate) state.recipes[index].lastTried = new Date() if (setDate) state.recipes[index].lastTried = new Date()
EnRecipesDB.updateDocument(id, recipe) EnRecipesDB.updateDocument(id, recipe)
}, },
setRecipeAsTried(state, { id, recipe }) { setRecipeAsTried(state, {
id,
recipe
}) {
let index = state.recipes.indexOf( let index = state.recipes.indexOf(
state.recipes.filter((e) => e.id === id)[0] state.recipes.filter((e) => e.id === id)[0]
) )
@ -362,7 +417,10 @@ export default new Vuex.Store({
state.recipes[index].lastTried = new Date() state.recipes[index].lastTried = new Date()
EnRecipesDB.updateDocument(state.recipes[index].id, state.recipes[index]) EnRecipesDB.updateDocument(state.recipes[index].id, state.recipes[index])
}, },
renameCategory(state, { current, updated }) { renameCategory(state, {
current,
updated
}) {
let lowercase = state.categories.map((e) => e.toLowerCase()) let lowercase = state.categories.map((e) => e.toLowerCase())
if (lowercase.indexOf(updated.toLowerCase()) == -1) { if (lowercase.indexOf(updated.toLowerCase()) == -1) {
state.userCategories.push(updated) state.userCategories.push(updated)
@ -384,7 +442,10 @@ export default new Vuex.Store({
setCurrentComponent(state, comp) { setCurrentComponent(state, comp) {
state.currentComponent = comp state.currentComponent = comp
}, },
unSyncCombinations(state, { id, combinations }) { unSyncCombinations(state, {
id,
combinations
}) {
state.recipes.forEach((e, i) => { state.recipes.forEach((e, i) => {
if (combinations.includes(e.id)) { if (combinations.includes(e.id)) {
state.recipes[i].combinations.splice(e.combinations.indexOf(id), 1) state.recipes[i].combinations.splice(e.combinations.indexOf(id), 1)
@ -394,71 +455,113 @@ export default new Vuex.Store({
}, },
}, },
actions: { actions: {
initializeRecipes({ commit }) { initializeRecipes({
commit
}) {
commit("initializeRecipes") commit("initializeRecipes")
}, },
initializeCategories({ commit }) { initializeCategories({
commit
}) {
commit("initializeCategories") commit("initializeCategories")
}, },
initializeYieldUnits({ commit }) { initializeYieldUnits({
commit
}) {
commit("initializeYieldUnits") commit("initializeYieldUnits")
}, },
initializeMealPlans({ commit }) { initializeMealPlans({
commit
}) {
commit("initializeMealPlans") commit("initializeMealPlans")
}, },
importRecipesAction({ commit }, recipes) { importRecipesAction({
commit
}, recipes) {
commit("importRecipes", recipes) commit("importRecipes", recipes)
}, },
importCategoriesAction({ commit }, categories) { importCategoriesAction({
commit
}, categories) {
commit("importCategories", categories) commit("importCategories", categories)
}, },
importYieldUnitsAction({ commit }, yieldUnits) { importYieldUnitsAction({
commit
}, yieldUnits) {
commit("importYieldUnits", yieldUnits) commit("importYieldUnits", yieldUnits)
}, },
importMealPlansAction({ commit }, mealPlans) { importMealPlansAction({
commit
}, mealPlans) {
commit("importMealPlans", mealPlans) commit("importMealPlans", mealPlans)
}, },
addRecipeAction({ commit }, recipe) { addRecipeAction({
commit
}, recipe) {
commit("addRecipe", recipe) commit("addRecipe", recipe)
}, },
addYieldUnitAction({ commit }, yieldUnit) { addYieldUnitAction({
commit
}, yieldUnit) {
commit("addYieldUnit", yieldUnit) commit("addYieldUnit", yieldUnit)
}, },
addCategoryAction({ commit }, category) { addCategoryAction({
commit
}, category) {
commit("addCategory", category) commit("addCategory", category)
}, },
addMealPlanAction({ commit }, mealPlan) { addMealPlanAction({
commit
}, mealPlan) {
commit("addMealPlan", mealPlan) commit("addMealPlan", mealPlan)
}, },
deleteMealPlanAction({ commit }, mealPlan) { deleteMealPlanAction({
commit
}, mealPlan) {
commit("deleteMealPlan", mealPlan) commit("deleteMealPlan", mealPlan)
}, },
deleteRecipeAction({ commit }, recipe) { deleteRecipeAction({
commit
}, recipe) {
commit("deleteRecipe", recipe) commit("deleteRecipe", recipe)
}, },
overwriteRecipeAction({ commit }, updatedRecipe) { overwriteRecipeAction({
commit
}, updatedRecipe) {
commit("overwriteRecipe", updatedRecipe) commit("overwriteRecipe", updatedRecipe)
}, },
toggleStateAction({ commit }, toggledRecipe) { toggleStateAction({
commit
}, toggledRecipe) {
commit("toggleState", toggledRecipe) commit("toggleState", toggledRecipe)
}, },
setRecipeAsTriedAction({ commit }, recipe) { setRecipeAsTriedAction({
commit
}, recipe) {
commit("setRecipeAsTried", recipe) commit("setRecipeAsTried", recipe)
}, },
setLastTriedDateAction({ commit }, index) { setLastTriedDateAction({
commit
}, index) {
commit("setLastTriedDate", index) commit("setLastTriedDate", index)
}, },
renameCategoryAction({ commit }, category) { renameCategoryAction({
commit
}, category) {
commit("renameCategory", category) commit("renameCategory", category)
}, },
setCurrentComponentAction({ commit }, comp) { setCurrentComponentAction({
commit
}, comp) {
commit("setCurrentComponent", comp) commit("setCurrentComponent", comp)
}, },
unSyncCombinationsAction({ commit }, combinations) { unSyncCombinationsAction({
commit
}, combinations) {
commit("unSyncCombinations", combinations) commit("unSyncCombinations", combinations)
}, },
}, },

View file

@ -0,0 +1,5 @@
- Fixed several bugs causing app crashes
- Minor UI improvements
- Application language support
- Added Tamil
- More languages coming soon!

View file

@ -5,9 +5,9 @@ EnRecipes is an easy to use, privacy-friendly digital cookbook that lets you cre
▸ Add photo, notes and combinations to your recipes ▸ Add photo, notes and combinations to your recipes
▸ Organise your recipes by category ▸ Organise your recipes by category
▸ Quickly search for your recipes ▸ Quickly search for your recipes
▸ Mark recipes as favorites and add them to your Try Later list ▸ Mark recipes as favourites and add them to your Try Later list
▸ Scale your recipe ingredients to serve more or less people ▸ Scale your recipe ingredients to serve more or less people
▸ Get notified of the last time you tried a recipe ▸ Get notified of when you last tried a recipe
▸ Share your recipe to anyone by any means as a nicely formatted message. You can share the recipe photo too. ▸ Share your recipe to anyone by any means as a nicely formatted message. You can share the recipe photo too.
▸ Create meal plans ▸ Create meal plans
▸ Import/Export recipes ▸ Import/Export recipes
@ -16,7 +16,5 @@ EnRecipes is an easy to use, privacy-friendly digital cookbook that lets you cre
▸ No internet access is required and never asks for any unwanted permissions ▸ No internet access is required and never asks for any unwanted permissions
▸ 100% free and open-source ▸ 100% free and open-source
Lots of interesting features on the way...
<b>Credits</b> <b>Credits</b>
This app was written in my free time using NativeScript. I would like to thank all those people who helped me understand the concepts during the process and my special thanks to the NativeScript team and the community. This app was written in my free time using NativeScript. I would like to thank all those people who helped me understand the concepts during the process and my special thanks to the NativeScript team and the community.

View file

@ -0,0 +1 @@
module.exports = require("@nativescript/localize/hooks/before-checkForChanges.js");

View file

@ -0,0 +1 @@
module.exports = require("@nativescript/localize/hooks/before-watchPatterns.js");

View file

@ -2,7 +2,7 @@ import { NativeScriptConfig } from "@nativescript/core"
export default { export default {
id: "com.vishnuraghav.enrecipes", id: "com.vishnuraghav.enrecipes",
appResourcesPath: "app/App_Resources", appResourcesPath: "app/resources",
android: { android: {
v8Flags: "--expose_gc", v8Flags: "--expose_gc",
markingMode: "none", markingMode: "none",

80
package-lock.json generated
View file

@ -1131,6 +1131,17 @@
"mkdirp": "^1.0.4" "mkdirp": "^1.0.4"
} }
}, },
"@nativescript/localize": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@nativescript/localize/-/localize-5.0.2.tgz",
"integrity": "sha512-TokwtXhb3oROr3ST0vCsuDHkTl+pi2caNEkTZNTlXXt5lBd4qyLpooo6ua0O7rVZ6VDcFD9rmJU0sPyNJCMvcw==",
"requires": {
"mkdirp": "^1.0.4",
"shorthash": "0.0.2",
"simple-plist": "^1.1.0",
"sprintf-js": "^1.1.1"
}
},
"@nativescript/social-share": { "@nativescript/social-share": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@nativescript/social-share/-/social-share-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@nativescript/social-share/-/social-share-2.0.1.tgz",
@ -2032,8 +2043,7 @@
"base64-js": { "base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
"dev": true
}, },
"bcrypt-pbkdf": { "bcrypt-pbkdf": {
"version": "1.0.2", "version": "1.0.2",
@ -2056,6 +2066,11 @@
"tryer": "^1.0.1" "tryer": "^1.0.1"
} }
}, },
"big-integer": {
"version": "1.6.48",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
"integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w=="
},
"big.js": { "big.js": {
"version": "5.2.2", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@ -2135,6 +2150,22 @@
} }
} }
}, },
"bplist-creator": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.8.tgz",
"integrity": "sha512-Za9JKzD6fjLC16oX2wsXfc+qBEhJBJB1YPInoAQpMLhDuj5aVOv1baGeIQSq1Fr3OCqzvsoQcSBSwGId/Ja2PA==",
"requires": {
"stream-buffers": "~2.2.0"
}
},
"bplist-parser": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz",
"integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==",
"requires": {
"big-integer": "^1.6.44"
}
},
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -6220,6 +6251,16 @@
"find-up": "^4.0.0" "find-up": "^4.0.0"
} }
}, },
"plist": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz",
"integrity": "sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==",
"requires": {
"base64-js": "^1.2.3",
"xmlbuilder": "^9.0.7",
"xmldom": "0.1.x"
}
},
"posix-character-classes": { "posix-character-classes": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@ -7333,12 +7374,27 @@
"rechoir": "^0.6.2" "rechoir": "^0.6.2"
} }
}, },
"shorthash": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/shorthash/-/shorthash-0.0.2.tgz",
"integrity": "sha1-WbJo7sveWQOLMNogK8+93rLEpOs="
},
"signal-exit": { "signal-exit": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
"dev": true "dev": true
}, },
"simple-plist": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.1.1.tgz",
"integrity": "sha512-pKMCVKvZbZTsqYR6RKgLfBHkh2cV89GXcA/0CVPje3sOiNOnXA8+rp/ciAMZ7JRaUdLzlEM6JFfUn+fS6Nt3hg==",
"requires": {
"bplist-creator": "0.0.8",
"bplist-parser": "0.2.0",
"plist": "^3.0.1"
}
},
"slash": { "slash": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@ -7569,6 +7625,11 @@
"extend-shallow": "^3.0.0" "extend-shallow": "^3.0.0"
} }
}, },
"sprintf-js": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
},
"sshpk": { "sshpk": {
"version": "1.16.1", "version": "1.16.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
@ -7641,6 +7702,11 @@
"readable-stream": "^2.0.2" "readable-stream": "^2.0.2"
} }
}, },
"stream-buffers": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz",
"integrity": "sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ="
},
"stream-each": { "stream-each": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
@ -9082,6 +9148,16 @@
"async-limiter": "~1.0.0" "async-limiter": "~1.0.0"
} }
}, },
"xmlbuilder": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
},
"xmldom": {
"version": "0.1.31",
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz",
"integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ=="
},
"xtend": { "xtend": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",

View file

@ -12,6 +12,7 @@
"@nativescript-community/ui-material-progress": "^5.0.30", "@nativescript-community/ui-material-progress": "^5.0.30",
"@nativescript-community/ui-material-ripple": "^5.0.30", "@nativescript-community/ui-material-ripple": "^5.0.30",
"@nativescript/core": "~7.0.0", "@nativescript/core": "~7.0.0",
"@nativescript/localize": "^5.0.2",
"@nativescript/social-share": "^2.0.1", "@nativescript/social-share": "^2.0.1",
"@nativescript/theme": "^3.0.0", "@nativescript/theme": "^3.0.0",
"@nativescript/zip": "^5.0.0", "@nativescript/zip": "^5.0.0",

Some files were not shown because too many files have changed in this diff Show more