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 {
@ -260,17 +255,20 @@ ActionBar {
padding: 2 16 0 0; padding: 2 16 0 0;
font-size: 16; font-size: 16;
vertical-alignment: center; vertical-alignment: center;
&.bx{ &.bx {
padding: 0 0 0 16; padding: 0 0 0 16;
} }
} }
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;
@ -356,8 +354,8 @@ RadListView {
border-radius: 4 0 0 4; border-radius: 4 0 0 4;
// prettier-ignore // prettier-ignore
Image { Image {
border-radius: 4 0 0 4; border-radius: 4 0 0 4;
} }
} }
} }
.swipe-item { .swipe-item {
@ -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

@ -1,95 +1,71 @@
<template> <template>
<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" </GridLayout>
:text="icon.menu" </ActionBar>
automationText="Back" <ScrollView @scroll="onScroll">
@tap="showDrawer" <StackLayout class="main-container">
col="0" <StackLayout horizontalAlignment="center" orientation="horizontal" class="appIconContainer">
/> <Image src="res://logo" class="appIcon" stretch="aspectFit" />
<Label class="title orkm" text="About" col="1" />
</GridLayout>
</ActionBar>
<ScrollView @scroll="onScroll">
<StackLayout class="main-container">
<StackLayout
horizontalAlignment="center"
orientation="horizontal"
class="appIconContainer"
>
<Image src="res://logo" class="appIcon" stretch="aspectFit" />
</StackLayout>
<StackLayout class="m-8"></StackLayout>
<GridLayout columns="auto, *" class="option">
<Label col="0" class="bx" :text="icon.info" />
<StackLayout col="1">
<Label text="Version" />
<Label :text="getVersion" class="info" textWrap="true" />
</StackLayout>
</GridLayout>
<GridLayout columns="auto, *" class="option">
<MDRipple
colSpan="2"
@tap="openURL('https://github.com/vishnuraghavb/enrecipes')"
/>
<Label col="0" class="bx" :text="icon.link" />
<Label
verticalAlignment="center"
col="1"
text="View project on GitHub"
/>
</GridLayout>
<GridLayout columns="auto, *" class="option">
<MDRipple colSpan="2" @tap="openURL('https://t.me/enrecipes')" />
<Label col="0" class="bx" :text="icon.telegram" />
<StackLayout col="1">
<Label text="Join the Telegram group" />
<Label
text="for reporting issues, suggestions and feedback"
class="info"
textWrap="true"
/>
</StackLayout>
</GridLayout>
<StackLayout class="hr m-10"></StackLayout>
<Label text="Author" class="group-header orkm" />
<GridLayout columns="auto, *" class="option">
<MDRipple
colSpan="2"
@tap="openURL('https://www.vishnuraghav.com')"
/>
<Label col="0" class="bx" :text="icon.user" />
<Label verticalAlignment="center" col="1" text="Vishnu Raghav B" />
</GridLayout>
<GridLayout columns="auto, *" class="option">
<MDRipple
colSpan="2"
@tap="openURL('https://github.com/vishnuraghavb')"
/>
<Label col="0" class="bx" :text="icon.link" />
<Label verticalAlignment="center" col="1" text="Follow on GitHub" />
</GridLayout>
<GridLayout columns="auto, *" class="option">
<MDRipple
colSpan="2"
@tap="openURL('https://mastodon.social/@vishnuraghavb')"
/>
<Label col="0" class="bx" :text="icon.link" />
<Label verticalAlignment="center" col="1" text="Follow on Mastodon" />
</GridLayout>
</StackLayout> </StackLayout>
</ScrollView> <StackLayout class="m-8"></StackLayout>
</Page> <GridLayout columns="auto, *" class="option">
<Label col="0" class="bx" :text="icon.info" />
<StackLayout col="1">
<Label :text="'Version' | L" />
<Label :text="getVersion" class="info" textWrap="true" />
</StackLayout>
</GridLayout>
<GridLayout columns="auto, *" class="option">
<MDRipple colSpan="2" @tap="openURL('https://github.com/vishnuraghavb/enrecipes')" />
<Label col="0" class="bx" :text="icon.link" />
<Label verticalAlignment="center" col="1" :text="'View project on GitHub' | L" textWrap="true" />
</GridLayout>
<GridLayout columns="auto, *" class="option">
<MDRipple colSpan="2" @tap="openURL('https://t.me/enrecipes')" />
<Label col="0" class="bx" :text="icon.telegram" />
<StackLayout col="1">
<Label :text="'Join the Telegram group' | L" textWrap="true" />
<Label :text="'for reporting issues, suggestions and feedback' | L" class="info" textWrap="true" />
</StackLayout>
</GridLayout>
<StackLayout class="hr m-10"></StackLayout>
<Label :text="'Author' | L" class="group-header orkm" />
<GridLayout columns="auto, *" class="option">
<MDRipple colSpan="2" @tap="openURL('https://www.vishnuraghav.com')" />
<Label col="0" class="bx" :text="icon.user" />
<Label verticalAlignment="center" col="1" :text="'Vishnu Raghav B' | L" textWrap="true" />
</GridLayout>
<GridLayout columns="auto, *" class="option">
<MDRipple colSpan="2" @tap="openURL('https://github.com/vishnuraghavb')" />
<Label col="0" class="bx" :text="icon.link" />
<Label verticalAlignment="center" col="1" :text="'Follow on GitHub' | L" textWrap="true" />
</GridLayout>
<GridLayout columns="auto, *" class="option">
<MDRipple colSpan="2" @tap="openURL('https://mastodon.social/@vishnuraghavb')" />
<Label col="0" class="bx" :text="icon.link" />
<Label verticalAlignment="center" col="1" :text="'Follow on Mastodon' | L" textWrap="true" />
</GridLayout>
</StackLayout>
</ScrollView>
</Page>
</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,141 +1,60 @@
<template> <template>
<Page <Page @loaded="onPageLoad" actionBarHidden="true" :androidStatusBarBackground="appTheme == 'Light' ? '#f1f3f5' : '#212529'">
@loaded="onPageLoad"
actionBarHidden="true"
: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">
<StackLayout row="0">
<GridLayout
rows="48"
columns="auto, 24, *"
v-for="(item, index) in topmenu"
:key="index"
class="sd-item orkm"
:class="{
'selected-sd-item': currentComponent === item.component,
}"
>
<MDRipple
row="0"
colSpan="3"
@tap="navigateTo(item.component, false, false)"
/>
<Label col="0" row="0" class="bx" :text="icon[item.icon]" />
<Label col="2" row="0" :text="item.title" />
</GridLayout>
<StackLayout class="hr m-8"></StackLayout>
<GridLayout
rows="48"
columns="auto, 24, *"
class="sd-item orkm"
:class="{
'selected-sd-item': currentComponent === 'MealPlanner',
}"
>
<MDRipple
row="0"
colSpan="3"
@tap="navigateTo(MealPlanner, true, false)"
/>
<Label col="0" row="0" class="bx" :text="icon.calendar" /> <RadSideDrawer allowEdgeSwipe="true" showOverNavigation="true" ref="drawer" id="sideDrawer" drawerContentSize="270" gesturesEnabled="true" drawerTransition="SlideInOnTopTransition">
<Label col="2" row="0" text="Meal Planner" /> <GridLayout rows="*, auto" columns="*" ~drawerContent class="sd">
</GridLayout> <StackLayout row="0">
<StackLayout class="hr m-8"></StackLayout> <GridLayout rows="48" columns="auto, 24, *" v-for="(item, index) in topmenu" :key="index" class="sd-item orkm" :class="{
<GridLayout 'selected-sd-item': currentComponent === item.component,
class="sd-group-header orkm" }">
rows="auto" <MDRipple row="0" colSpan="3" @tap="navigateTo(item.component, item.component, false, false)" />
columns="*, auto" <Label col="0" row="0" class="bx" :text="icon[item.icon]" />
v-if="categoriesWithRecipes.length" <Label col="2" row="0" :text="`${item.title}` | L" />
> </GridLayout>
<Label verticalAlignment="center" col="0" text="Categories" /> <StackLayout class="hr" margin="8"></StackLayout>
<MDButton <GridLayout rows="48" columns="auto, 24, *" class="sd-item orkm" :class="{
variant="text" 'selected-sd-item': currentComponent === 'MealPlanner',
@tap="toggleCatEdit" }">
col="2" <MDRipple row="0" colSpan="3" @tap="navigateTo(MealPlanner, 'MealPlanner', true, false)" />
:text="editCategory ? 'DONE' : 'RENAME'"
/> <Label col="0" row="0" class="bx" :text="icon.calendar" />
</GridLayout> <Label col="2" row="0" :text="'Meal Planner' | L" />
<ScrollView height="100%"> </GridLayout>
<StackLayout> <StackLayout class="hr" margin="8"></StackLayout>
<GridLayout <GridLayout class="sd-group-header orkm" rows="auto" columns="*, auto" v-if="categoriesWithRecipes.length">
v-for="(item, index) in categoriesWithRecipes" <Label verticalAlignment="center" col="0" :text="'Categories' | L" />
:key="index" <MDButton variant="text" @tap="toggleCatEdit" col="2" :text="`${editCategory ? 'DONE' : 'RENAME'}` | L" />
class="sd-item orkm" </GridLayout>
:class="{ <ScrollView height="100%">
<StackLayout>
<GridLayout 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" </GridLayout>
@tap="navigateTo(item, false, true)" </StackLayout>
/> </ScrollView>
<Label </StackLayout>
col="0" <StackLayout row="1">
class="bx" <StackLayout class="hr" margin="0 8 8"></StackLayout>
:text="icon.label" <GridLayout class="sd-item orkm" :class="{
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>
</StackLayout>
</ScrollView>
</StackLayout>
<StackLayout row="1">
<StackLayout class="hr m-10"></StackLayout>
<GridLayout
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" <Label class="bx" col="0" :text="icon[item.icon]" />
rows="48" <Label col="2" :text="`${item.title}` | L" />
columns="auto, 24, *" </GridLayout>
> </StackLayout>
<MDRipple </GridLayout>
colSpan="3" <Frame ~mainContent id="main-frame">
@tap="navigateTo(item.component, true, false)" <EnRecipes ref="enrecipes" :filterFavourites="filterFavourites" :filterTrylater="filterTrylater" :selectedCategory="selectedCategory" :closeDrawer="closeDrawer" :hijackGlobalBackEvent="hijackGlobalBackEvent"
/> :releaseGlobalBackEvent="releaseGlobalBackEvent" />
<Label class="bx" col="0" :text="icon[item.icon]" /> </Frame>
<Label col="2" :text="item.title" /> </RadSideDrawer>
</GridLayout> </Page>
</StackLayout>
</GridLayout>
<Frame ~mainContent id="main-frame">
<!-- Home -->
<EnRecipes
ref="enrecipes"
:filterFavorites="filterFavorites"
:filterTrylater="filterTrylater"
:selectedCategory="selectedCategory"
:closeDrawer="closeDrawer"
:hijackGlobalBackEvent="hijackGlobalBackEvent"
:releaseGlobalBackEvent="releaseGlobalBackEvent"
/>
</Frame>
</RadSideDrawer>
</Page>
</template> </template>
<script> <script>
@ -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",
@ -201,68 +124,68 @@ export default {
MealPlanner, MealPlanner,
}, },
computed: { computed: {
...mapState([ ...mapState( [
"icon", "icon",
"recipes", "recipes",
"categories", "categories",
"yieldUnits", "yieldUnits",
"mealPlans", "mealPlans",
"currentComponent", "currentComponent",
]), ] ),
categoriesWithRecipes() { categoriesWithRecipes() {
let arr = this.recipes.map((e) => { let arr = this.recipes.map( ( e ) => {
return e.category return e.category
}) } )
return [...new Set(arr)] return [ ...new Set( arr ) ]
}, },
}, },
methods: { methods: {
...mapActions([ ...mapActions( [
"setCurrentComponentAction", "setCurrentComponentAction",
"initializeRecipes", "initializeRecipes",
"initializeCategories", "initializeCategories",
"initializeYieldUnits", "initializeYieldUnits",
"initializeMealPlans", "initializeMealPlans",
"renameCategoryAction", "renameCategoryAction",
]), ] ),
onPageLoad() { onPageLoad() {
if (this.appTheme === "Light") { if ( this.appTheme === "Light" ) {
const View = android.view.View const View = android.view.View
const window = Application.android.startActivity.getWindow() const window = Application.android.startActivity.getWindow()
const decorView = window.getDecorView() const decorView = window.getDecorView()
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) decorView.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR )
} }
}, },
// 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) { renameCategory( category ) {
this.setCurrentComponentAction(comp)
},
renameCategory(category) {
this.releaseGlobalBackEvent() this.releaseGlobalBackEvent()
this.$showModal(PromptDialog, { this.$showModal( PromptDialog, {
props: { props: {
title: `Rename category`, title: `Rename category`,
text: category, text: category,
action: "RENAME", action: "RENAME",
}, },
}).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 )
} }
}) } )
}, },
setSelectedCategory(e) { setSelectedCategory( e ) {
this.selectedCategory = e.item this.selectedCategory = e.item
this.closeDrawer() this.closeDrawer()
}, },
@ -284,67 +207,68 @@ export default {
this.globalBackEvent this.globalBackEvent
) )
}, },
globalBackEvent(args) { globalBackEvent( args ) {
function preventDefault() { function preventDefault() {
args.cancel = true args.cancel = true
} }
let vm = this if ( this.$refs.drawer && this.$refs.drawer.nativeView.getIsOpen() ) {
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()) {
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 (isTrueComponent) { if ( title !== this.currentComponent ) {
this.$navigateTo(to, { if ( isTrueComponent ) {
frame: "main-frame", this.$navigateTo( to, {
backstackVisible: false, frame: "main-frame",
}) backstackVisible: false
this.closeDrawer() } )
} else if (!this.editCategory || !isCategory) { this.closeDrawer()
this.releaseGlobalBackEvent() }
this.hijackGlobalBackEvent() else if ( !this.editCategory || !isCategory ) {
this.setComponent(to) this.releaseGlobalBackEvent()
this.$navigateBack({ frame: "main-frame", backstackVisible: false }) this.hijackGlobalBackEvent()
this.filterFavorites = to === "Favorites" ? true : false this.setCurrentComponentAction( to )
this.filterTrylater = to === "Try Later" ? true : false this.$navigateBack( {
this.selectedCategory = isCategory ? to : null frame: "main-frame",
this.$refs.enrecipes.updateFilter() backstackVisible: false
} )
this.filterFavourites = to === "Favourites" ? true : false
this.filterTrylater = to === "Try Later" ? true : false
this.selectedCategory = isCategory ? to : null
this.$refs.enrecipes.updateFilter()
this.closeDrawer()
}
this.editCategory = false
}
else {
this.closeDrawer() this.closeDrawer()
} }
this.editCategory = false
}, },
}, },
created() { created() {
this.appTheme = ApplicationSettings.getString("appTheme", "Light") this.appTheme = ApplicationSettings.getString( "appTheme", "Light" )
setTimeout((e) => { setTimeout( ( e ) => {
Theme.setMode(Theme[this.appTheme]) Theme.setMode( Theme[ this.appTheme ] )
}, 10) }, 10 )
if (!this.recipes.length) this.initializeRecipes() if ( !this.recipes.length ) this.initializeRecipes()
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>

File diff suppressed because it is too large Load diff

View file

@ -1,246 +1,123 @@
<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, *" </GridLayout>
verticalAlignment="center" <GridLayout v-else columns="auto, *, auto, auto">
> <MDButton class="bx" col="0" variant="text" @tap="showDrawer" :text="icon.menu" automationText="Back" />
<MDButton <Label class="title orkm" :text="`${currentComponent}` | L" col="1" />
class="bx" <MDButton v-if="recipes.length" class="bx" :text="icon.search" variant="text" col="2" @tap="openSearch" />
:text="icon.back" <MDButton v-if="recipes.length" class="bx" :text="icon.sort" variant="text" col="3" @tap="sortDialog" />
variant="text" </GridLayout>
automationText="Back" </ActionBar>
col="0" <AbsoluteLayout>
@tap="closeSearch" <RadListView ref="listView" itemHeight="112" for="recipe in recipes" swipeActions="true" @itemSwipeProgressChanged="onSwiping" @itemSwipeProgressEnded="onSwipeEnded" @scrolled="onScroll" :filteringFunction="filterFunction"
/> :sortingFunction="sortFunction">
<SearchBar <v-template>
col="1" <GridLayout class="recipeItem" rows="112" columns="112, *" androidElevation="1">
hint="Search" <MDRipple colSpan="2" @tap="viewRecipe(recipe.id)" />
v-model="searchQuery" <GridLayout class="imageHolder card" rows="112" columns="112">
@textChange="updateFilter" <Image row="0" col="0" v-if="recipe.imageSrc" :src="recipe.imageSrc" stretch="aspectFill" decodeWidth="112" decodeHeight="112" loadMode="async" />
@clear="clearSearch" <Label v-else row="0" col="0" horizontalAlignment="center" class="bx" fontSize="56" :text="icon.image" />
/> </GridLayout>
</GridLayout> <StackLayout class="recipeInfo" col="1">
<!-- Home Actionbar --> <Label :text="`${recipe.category}` | L" class="orkm category" />
<GridLayout v-else columns="auto, *, auto, auto"> <Label :text="recipe.title" class="orkm title" />
<MDButton <StackLayout class="timeContainer" orientation="horizontal">
class="bx" <Label class="bx small" :text="icon.time" />
col="0" <Label class="time" :text="
variant="text"
@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>
</ActionBar>
<AbsoluteLayout>
<RadListView
ref="listView"
itemHeight="112"
for="recipe in recipes"
swipeActions="true"
@itemSwipeProgressChanged="onSwiping"
@itemSwipeProgressEnded="onSwipeEnded"
@scrolled="onScroll"
:filteringFunction="filterFunction"
:sortingFunction="sortFunction"
>
<v-template>
<GridLayout
class="recipeItem"
rows="112"
columns="112, *"
androidElevation="1"
>
<MDRipple colSpan="2" @tap="viewRecipe(recipe.id)" />
<GridLayout class="imageHolder card" rows="112" columns="112">
<Image
row="0"
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>
<StackLayout class="recipeInfo" col="1">
<Label :text="recipe.category" class="orkm category" />
<Label :text="recipe.title" class="orkm title" />
<StackLayout class="timeContainer" orientation="horizontal">
<Label class="bx small" :text="icon.time" />
<Label
class="time"
:text="
`${ `${
formattedTotalTime(recipe.prepTime, recipe.cookTime).time formattedTotalTime(recipe.prepTime, recipe.cookTime).time
}` }`
" " />
/>
</StackLayout>
</StackLayout> </StackLayout>
</GridLayout> </StackLayout>
</v-template> </GridLayout>
<v-template name="itemswipe"> </v-template>
<GridLayout columns="*, auto"> <v-template name="itemswipe">
<StackLayout id="delete-action" col="1" class="swipe-item right"> <GridLayout columns="*, auto">
<Label class="bx" padding="8 0 0 6" :text="icon.trash" /> <StackLayout id="delete-action" col="1" class="swipe-item right">
</StackLayout> <Label class="bx" padding="8 0 0 6" :text="icon.trash" />
</GridLayout> </StackLayout>
</v-template> </GridLayout>
<v-template name="footer"> </v-template>
<StackLayout height="80"></StackLayout> <v-template name="footer">
</v-template> <StackLayout height="80"></StackLayout>
</RadListView> </v-template>
<GridLayout rows="*, auto, *, 88" columns="*" class="emptyStateContainer"> </RadListView>
<StackLayout <GridLayout rows="*, auto, *, 88" columns="*" class="emptyStateContainer">
row="1" <StackLayout row="1" class="emptyState" v-if="
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="title orkm" :text="'Start adding your recipes!' | L" textWrap="true" />
<Label class="bx icon" :text="icon.plusCircle" /> <StackLayout orientation="horizontal" horizontalAlignment="center">
<Label <Label :text="'Use the plus button to add one' | L" textWrap="true" />
class="title orkm"
text="Start adding your recipes!"
textWrap="true"
/>
<StackLayout orientation="horizontal" horizontalAlignment="center">
<Label text="Use the " />
<Label class="bx" :text="icon.plus" />
<Label text=" button to add one" />
</StackLayout>
</StackLayout> </StackLayout>
<StackLayout </StackLayout>
row="1" <StackLayout row="1" class="emptyState" v-if="!filteredRecipes.length && filterTrylater && !searchQuery">
class="emptyState" <Label class="bx icon" :text="icon.trylaterOutline" textWrap="true" />
v-if="!filteredRecipes.length && filterTrylater && !searchQuery" <Label class="title orkm" :text="'All done!' | L" textWrap="true" />
> <Label :text="'Recipes you mark as try later will be listed here' | L" textWrap="true" />
<Label class="bx icon" :text="icon.trylaterOutline" textWrap="true" /> </StackLayout>
<Label class="title orkm" text="All done!" textWrap="true" /> <StackLayout row="1" class="emptyState" v-if="!filteredRecipes.length && filterFavourites && !searchQuery">
<Label <Label class="bx icon" :text="icon.heartOutline" textWrap="true" />
text="Recipes you mark as try later will be listed here" <Label class="title orkm" :text="'No favourites yet' | L" textWrap="true" />
textWrap="true" <Label :text="'Recipes you mark as favourite will be listed here' | L" textWrap="true" />
/> </StackLayout>
<StackLayout row="1" class="emptyState" v-if="!filteredRecipes.length && !searchQuery && selectedCategory">
<Label class="bx icon" :text="icon.labelOutline" textWrap="true" />
<Label class="title orkm" :text="'Category looks empty' | L" textWrap="true" />
<StackLayout orientation="horizontal" horizontalAlignment="center">
<Label :text="'Use the plus button to add one' | L" textWrap="true" />
</StackLayout> </StackLayout>
<StackLayout </StackLayout>
row="1" <StackLayout row="1" class="emptyState" v-if="!filteredRecipes.length && searchQuery">
class="emptyState" <Label class="bx icon" :text="icon.search" textWrap="true" />
v-if="!filteredRecipes.length && filterFavorites && !searchQuery" <Label class="title orkm" :text="'No recipes found' | L" textWrap="true" />
> <Label :text="`${noResultFor}` | L" textWrap="true" />
<Label class="bx icon" :text="icon.heartOutline" textWrap="true" /> </StackLayout>
<Label class="title orkm" text="No favorites yet" textWrap="true" /> </GridLayout>
<Label <GridLayout id="btnFabContainer" rows="*, auto" columns="*, auto">
text="Recipes you mark as favorite will be listed here" <transition name="bounce">
textWrap="true" <MDFloatingActionButton v-if="showFAB" row="1" col="1" class="bx fab-button" src="res://plus" @tap="addRecipe" />
/> </transition>
</StackLayout> </GridLayout>
<StackLayout </AbsoluteLayout>
row="1" </Page>
class="emptyState"
v-if="!filteredRecipes.length && !searchQuery && selectedCategory"
>
<Label class="bx icon" :text="icon.labelOutline" textWrap="true" />
<Label
class="title orkm"
text="Category looks empty"
textWrap="true"
/>
<StackLayout orientation="horizontal" horizontalAlignment="center">
<Label text="Use the " textWrap="true" />
<Label class="bx" :text="icon.plus" />
<Label text=" button to add a recipe" textWrap="true" />
</StackLayout>
</StackLayout>
<StackLayout
row="1"
class="emptyState"
v-if="!filteredRecipes.length && searchQuery"
>
<Label class="bx icon" :text="icon.search" textWrap="true" />
<Label class="title orkm" text="No recipes found" textWrap="true" />
<Label
:text="
`Your search &quot;${searchQuery}&quot; did not match any recipes${noResultFor}`
"
textWrap="true"
/>
</StackLayout>
</GridLayout>
<GridLayout id="btnFabContainer" rows="*, auto" columns="*, auto">
<transition name="bounce">
<MDFloatingActionButton
v-if="showFAB"
row="1"
col="1"
class="bx fab-button"
src="res://plus"
@tap="addRecipe"
/>
</transition>
</GridLayout>
</AbsoluteLayout>
</Page>
</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 switch ( this.sortType ) {
let ld1 = new Date(item.lastModified)
let ld2 = new Date(otherItem.lastModified)
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" );
if (data.x < -swipeLimits.threshold) { swipeLimits.right = rightItem.getMeasuredWidth() - 12;
this.rightAction = true swipeLimits.threshold = swipeLimits.right - 6;
swipeView.notifySwipeToExecuteFinished() if ( data.x < -swipeLimits.threshold ) {
this.rightAction = true;
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) => {
if (action) {
this.deleteRecipeAction({ index, id: recipeID })
} }
this.deletionDialogActive = false } ).then( action => {
}) if ( action ) {
}, this.deleteRecipeAction( {
index,
id: recipeID
} );
}
this.deletionDialogActive = false;
} );
}
}, },
mounted() { mounted() {
this.showFAB = true this.showFAB = true;
}, }
} };
</script> </script>

View file

@ -1,90 +1,43 @@
<template> <template>
<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>
</ActionBar>
<GridLayout rows="280, *">
<RadCalendar
:androidElevation="viewIsScrolled ? 4 : 0"
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">
<StackLayout class="dayPlan">
<StackLayout
v-for="(mealType, index) in mealTimes"
:key="'mealType' + index"
class="plansContainer"
:class="mealType"
>
<GridLayout columns="*, auto" class="header">
<Label col="0" class="periodLabel orkm" :text="mealType" />
<MDButton
col="1"
variant="text"
class="bx addMeal"
:text="icon.plus"
@tap="addRecipe(mealType)"
/>
</GridLayout>
<GridLayout
class="recipes"
columns="*, auto"
v-for="(recipeID, index) in getRecipes[mealType]"
:key="mealType + index"
>
<MDRipple @tap="viewRecipe(recipeID)" />
<Label
verticalAlignment="center"
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>
</StackLayout>
</StackLayout>
</ScrollView>
</GridLayout> </GridLayout>
</Page> </ActionBar>
<GridLayout rows="280, *">
<RadCalendar :androidElevation="viewIsScrolled ? 4 : 0" class="orkm" row="0" ref="calendar" @loaded="onCalendarLoad" @dateSelected="onDateSelected" :viewMode="viewMode" :transitionMode="transitionMode" :selectionMode="selectionMode"
:eventsViewMode="eventsViewMode" :eventSource="getMealPlans"></RadCalendar>
<ScrollView row="1" width="100%" height="100%" @scroll="onScroll">
<StackLayout class="dayPlan">
<StackLayout v-for="(mealType, index) in mealTimes" :key="'mealType' + index" class="plansContainer" :class="mealType">
<GridLayout columns="*, auto" class="header">
<Label col="0" class="periodLabel orkm" :text="mealType | L" />
<MDButton col="1" variant="text" class="bx addMeal" :text="icon.plus" @tap="addRecipe(mealType)" />
</GridLayout>
<GridLayout class="recipes" columns="*, auto" v-for="(recipeID, index) in getRecipes[mealType]" :key="mealType + index">
<MDRipple @tap="viewRecipe(recipeID)" />
<Label verticalAlignment="center" 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>
</StackLayout>
</StackLayout>
</ScrollView>
</GridLayout>
</Page>
</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"
@ -112,7 +70,7 @@ export default {
return { return {
viewIsScrolled: false, viewIsScrolled: false,
appTheme: "Light", appTheme: "Light",
mealTimes: ["breakfast", "lunch", "dinner", "snacks"], mealTimes: [ "breakfast", "lunch", "dinner", "snacks" ],
eventList: [], eventList: [],
selectedDayMealPlans: [], selectedDayMealPlans: [],
viewMode: CalendarViewMode.Month, viewMode: CalendarViewMode.Month,
@ -120,18 +78,18 @@ export default {
selectionMode: CalendarSelectionMode.Single, selectionMode: CalendarSelectionMode.Single,
eventsViewMode: CalendarEventsViewMode.None, eventsViewMode: CalendarEventsViewMode.None,
color: { color: {
white: new Color("#ffffff"), white: new Color( "#ffffff" ),
gray1: new Color("#f1f3f5"), gray1: new Color( "#f1f3f5" ),
gray2: new Color("#e9ecef"), gray2: new Color( "#e9ecef" ),
gray3: new Color("#dee2e6"), gray3: new Color( "#dee2e6" ),
gray4: new Color("#ced4da"), gray4: new Color( "#ced4da" ),
gray5: new Color("#adb5bd"), gray5: new Color( "#adb5bd" ),
gray6: new Color("#868e96"), gray6: new Color( "#868e96" ),
gray7: new Color("#495057"), gray7: new Color( "#495057" ),
gray8: new Color("#343a40"), gray8: new Color( "#343a40" ),
gray9: new Color("#212529"), gray9: new Color( "#212529" ),
black: new Color("#111111"), black: new Color( "#111111" ),
orange: new Color("#ff5200"), orange: new Color( "#ff5200" ),
breakfast: "#ffb180", breakfast: "#ffb180",
lunch: "#ceff80", lunch: "#ceff80",
dinner: "#80ceff", dinner: "#80ceff",
@ -143,45 +101,45 @@ export default {
} }
}, },
computed: { computed: {
...mapState(["icon", "recipes", "mealPlans"]), ...mapState( [ "icon", "recipes", "mealPlans" ] ),
isLightMode() { isLightMode() {
return this.appTheme === "Light" return this.appTheme === "Light"
}, },
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
@ -242,31 +200,32 @@ export default {
return monthViewStyle return monthViewStyle
}, },
getRecipes() { getRecipes() {
if (this.selectedDayMealPlans.length) { if ( this.selectedDayMealPlans.length ) {
return this.selectedDayMealPlans.reduce((acc, e) => { return this.selectedDayMealPlans.reduce( ( acc, e ) => {
switch (e.startDate.getHours()) { switch ( e.startDate.getHours() ) {
case 0: //breakfast case 0: //breakfast
acc["breakfast"] = [...(acc["breakfast"] || []), e.title] acc[ "breakfast" ] = [ ...( acc[ "breakfast" ] || [] ), e.title ]
break break
case 5: //lunch case 5: //lunch
acc["lunch"] = [...(acc["lunch"] || []), e.title] acc[ "lunch" ] = [ ...( acc[ "lunch" ] || [] ), e.title ]
break break
case 10: //dinner case 10: //dinner
acc["dinner"] = [...(acc["dinner"] || []), e.title] acc[ "dinner" ] = [ ...( acc[ "dinner" ] || [] ), e.title ]
break break
case 15: //snacks case 15: //snacks
acc["snacks"] = [...(acc["snacks"] || []), e.title] acc[ "snacks" ] = [ ...( acc[ "snacks" ] || [] ), e.title ]
break break
default: default:
break break
} }
return acc return acc
}, {}) }, {} )
} else return 0 }
else return 0
}, },
getMealPlans() { getMealPlans() {
const getDate = (date) => { const getDate = ( date ) => {
let d = new Date(date) let d = new Date( date )
let result = new Date( let result = new Date(
d.getFullYear(), d.getFullYear(),
d.getMonth(), d.getMonth(),
@ -276,40 +235,43 @@ export default {
return result return result
} }
let events = [] let events = []
this.mealPlans.forEach((plan) => { this.mealPlans.forEach( ( plan ) => {
let e = new CalendarEvent( let e = new CalendarEvent(
plan.title, plan.title,
getDate(plan.startDate), getDate( plan.startDate ),
getDate(plan.endDate), getDate( plan.endDate ),
false, false,
new Color(plan.eventColor) new Color( plan.eventColor )
) )
events = [...events, e] events = [ ...events, e ]
}) } )
return events return events
}, },
}, },
methods: { methods: {
...mapActions([ ...mapActions( [
"setCurrentComponentAction", "setCurrentComponentAction",
"initializeMealPlans", "initializeMealPlans",
"addMealPlanAction", "addMealPlanAction",
"deleteMealPlanAction", "deleteMealPlanAction",
]), ] ),
onPageLoad(args) { onPageLoad( args ) {
this.setCurrentComponentAction("MealPlanner") const page = args.object;
page.bindingContext = new Observable();
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()
.setDoubleTapToChangeDisplayMode(false) .setDoubleTapToChangeDisplayMode( false )
args.object.android args.object.android
.getGestureManager() .getGestureManager()
.setPinchCloseToChangeDisplayMode(false) .setPinchCloseToChangeDisplayMode( false )
if (args.object.selectedDate == null) if ( args.object.selectedDate == null )
args.object.selectedDate = new Date() args.object.selectedDate = new Date()
if (args.object.nativeView.getEventAdapter()) { if ( args.object.nativeView.getEventAdapter() ) {
args.object.nativeView args.object.nativeView
.getEventAdapter() .getEventAdapter()
.getRenderer() .getRenderer()
@ -323,102 +285,102 @@ export default {
showDrawer() { showDrawer() {
utils.showDrawer() utils.showDrawer()
}, },
onScroll(args) { onScroll( args ) {
this.viewIsScrolled = args.scrollY ? true : false this.viewIsScrolled = args.scrollY ? true : false
}, },
getDate(index) { getDate( index ) {
let date = new Date() let date = new Date()
date.setDate(date.getDate() + index) date.setDate( date.getDate() + index )
return date.getTime() return date.getTime()
}, },
getDateString(days) { getDateString( days ) {
let date = new Date() let date = new Date()
date.setDate(date.getDate() + days) date.setDate( date.getDate() + days )
return date.toDateString().slice(0, -5) return date.toDateString().slice( 0, -5 )
}, },
getRecipeTitle(id) { getRecipeTitle( id ) {
let recipe = this.recipes.filter((e) => e.id === id)[0] let recipe = this.recipes.filter( ( e ) => e.id === id )[ 0 ]
return recipe ? recipe.title : "[Deleted Recipe]" return recipe ? recipe.title : "[Deleted Recipe]"
}, },
// NAVIGATION HANDLERS // NAVIGATION HANDLERS
viewRecipe(recipeID) { viewRecipe( recipeID ) {
let recipe = this.recipes.filter((e) => e.id === recipeID)[0] let recipe = this.recipes.filter( ( e ) => e.id === recipeID )[ 0 ]
if (recipe) { if ( recipe ) {
this.$navigateTo(ViewRecipe, { this.$navigateTo( ViewRecipe, {
props: { props: {
filterTrylater: true, filterTrylater: true,
recipeID, recipeID,
}, },
backstackVisible: false, backstackVisible: false,
}) } )
} }
}, },
// 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: {
title: "Select a recipe", title: "Select a recipe",
recipes: filteredRecipes, recipes: filteredRecipes,
}, },
}).then((recipeID) => { } ).then( ( recipeID ) => {
recipeID && this.newEvent(recipeID, mealType) recipeID && this.newEvent( recipeID, mealType )
}) } )
}, },
removeRecipeConfirm(mealType) { removeRecipeConfirm( mealType ) {
return this.$showModal(ConfirmDialog, { return this.$showModal( ConfirmDialog, {
props: { props: {
title: `Remove recipe from ${mealType}`, title: `Remove recipe from ${mealType}`,
cancelButtonText: "CANCEL", cancelButtonText: "CANCEL",
okButtonText: "REMOVE", okButtonText: "REMOVE",
}, },
}) } )
}, },
removeRecipe(mealType, recipeID) { removeRecipe( mealType, recipeID ) {
let startHour = { let startHour = {
breakfast: 0, breakfast: 0,
lunch: 5, lunch: 5,
dinner: 10, dinner: 10,
snacks: 15, snacks: 15,
} }
this.removeRecipeConfirm(mealType).then((res) => { this.removeRecipeConfirm( mealType ).then( ( res ) => {
if (res) { if ( res ) {
let actualMealPlan = this.selectedDayMealPlans.filter( let actualMealPlan = this.selectedDayMealPlans.filter(
(e) => ( e ) =>
e.startDate.getHours() === startHour[mealType] && e.startDate.getHours() === startHour[ mealType ] &&
e.title === recipeID e.title === recipeID
)[0] )[ 0 ]
let mealPlan = { let mealPlan = {
title: actualMealPlan.title, title: actualMealPlan.title,
startDate: actualMealPlan.startDate, startDate: actualMealPlan.startDate,
} }
this.deleteMealPlanAction(mealPlan) this.deleteMealPlanAction( mealPlan )
this.updateSelectedDatePlans() this.updateSelectedDatePlans()
} }
}) } )
}, },
// CALENDAR // CALENDAR
updateSelectedDatePlans() { updateSelectedDatePlans() {
let date = new Date(this.selectedDate) let date = new Date( this.selectedDate )
setTimeout(() => { setTimeout( () => {
this.selectedDayMealPlans = this.$refs.calendar.nativeView.getEventsForDate( this.selectedDayMealPlans = this.$refs.calendar.nativeView.getEventsForDate(
date date
) )
}, 100) }, 100 )
}, },
onDateSelected(args) { onDateSelected( args ) {
this.selectedDate = args.date this.selectedDate = args.date
this.selectedDayMealPlans = args.object.getEventsForDate(args.date) this.selectedDayMealPlans = args.object.getEventsForDate( args.date )
}, },
newEvent(recipeID, mealType) { newEvent( recipeID, mealType ) {
let date = new Date(this.selectedDate) let date = new Date( this.selectedDate )
const selectedDate = () => { const selectedDate = () => {
return { return {
y: date.getFullYear(), y: date.getFullYear(),
@ -426,45 +388,52 @@ 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 ),
end: new Date(y, m, d, 4), end: new Date( y, m, d, 4 ),
}, },
lunch: { lunch: {
start: new Date(y, m, d, 5), start: new Date( y, m, d, 5 ),
end: new Date(y, m, d, 9), end: new Date( y, m, d, 9 ),
}, },
dinner: { dinner: {
start: new Date(y, m, d, 10), start: new Date( y, m, d, 10 ),
end: new Date(y, m, d, 14), end: new Date( y, m, d, 14 ),
}, },
snacks: { snacks: {
start: new Date(y, m, d, 15), start: new Date( y, m, d, 15 ),
end: new Date(y, m, d, 19), end: new Date( y, m, d, 19 ),
}, },
} }
let event = new CalendarEvent( let event = new CalendarEvent(
recipeID, recipeID,
mealTime[mealType].start, mealTime[ mealType ].start,
mealTime[mealType].end, mealTime[ mealType ].end,
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() {
const date = new Date() const date = new Date()
this.$refs.calendar.goToDate(date) this.$refs.calendar.goToDate( date )
this.$refs.calendar.nativeView.selectedDate = date this.$refs.calendar.nativeView.selectedDate = date
}, },
}, },
created() { created() {
this.appTheme = ApplicationSettings.getString("appTheme", "Light") this.appTheme = ApplicationSettings.getString( "appTheme", "Light" )
let d = new Date() let d = new Date()
d.setHours(0, 0, 0) d.setHours( 0, 0, 0 )
this.selectedDate = d this.selectedDate = d
}, },
} }

View file

@ -1,73 +1,55 @@
<template> <template>
<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" </GridLayout>
:text="icon.menu" </ActionBar>
automationText="Back" <ScrollView @scroll="onScroll">
@tap="showDrawer" <StackLayout class="main-container">
col="0" <Label :text="'Interface' | L" class="group-header orkm" />
/> <GridLayout columns="auto, *" class="option">
<Label class="title orkm" text="Settings" col="1" /> <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>
</ActionBar> <GridLayout columns="auto, *" class="option">
<ScrollView @scroll="onScroll"> <MDRipple colSpan="2" @tap="selectThemes" />
<StackLayout class="main-container"> <Label col="0" verticalAlignment="center" class="bx" :text="icon.theme" />
<Label text="Interface" class="group-header orkm" /> <StackLayout col="1">
<GridLayout columns="auto, *" class="option"> <Label :text="'Theme' | L" />
<MDRipple colSpan="2" @tap="selectThemes" /> <Label :text="`${appTheme}` | L" class="info" />
<Label </StackLayout>
col="0" </GridLayout>
verticalAlignment="center" <StackLayout class="hr m-10"></StackLayout>
class="bx" <Label :text="'Database' | L" class="group-header orkm" />
:text="icon.theme" <GridLayout columns="auto, *" class="option">
/> <MDRipple colSpan="2" @tap="exportCheck" />
<StackLayout col="1"> <Label col="0" class="bx" :text="icon.export" />
<Label text="Theme" /> <StackLayout col="1">
<Label :text="appTheme" class="info" /> <Label :text="'Export a full backup' | L" textWrap="true" />
</StackLayout> <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" />
</GridLayout> <GridLayout class="progressContainer" v-else columns="*, 64">
<StackLayout class="hr m-10"></StackLayout> <MDProgress col="0" :value="backupProgress" maxValue="100"></MDProgress>
<Label text="Database" class="group-header orkm" /> <Label col="1" :text="` ${backupProgress}%`" />
<GridLayout columns="auto, *" class="option"> </GridLayout>
<!-- <MDRipple colSpan="2" @tap="folderPicker" /> --> </StackLayout>
<MDRipple colSpan="2" @tap="exportCheck" /> </GridLayout>
<Label col="0" class="bx" :text="icon.export" /> <GridLayout columns="auto, *" class="option">
<StackLayout col="1"> <MDRipple colSpan="2" @tap="importCheck" />
<Label text="Export a full backup" /> <Label col="0" class="bx" :text="icon.import" />
<Label <StackLayout col="1">
v-if="!backupInProgress" <Label :text="'Import from backup' | L" textWrap="true" />
text="Generates a zip file that contains all your data. This file can be imported back." <Label :text="'Supports full backups exported by this app' | L" class="info" textWrap="true" />
class="info" </StackLayout>
textWrap="true" </GridLayout>
/> </StackLayout>
<GridLayout class="progressContainer" v-else columns="*, 64"> </ScrollView>
<MDProgress </Page>
col="0"
:value="backupProgress"
maxValue="100"
></MDProgress>
<Label col="1" :text="` ${backupProgress}%`" />
</GridLayout>
</StackLayout>
</GridLayout>
<GridLayout columns="auto, *" class="option"
><MDRipple colSpan="2" @tap="importCheck" />
<Label col="0" class="bx" :text="icon.import" />
<StackLayout col="1">
<Label text="Import from backup" />
<Label
text="Supports full backups exported by this app"
class="info"
textWrap="true"
/>
</StackLayout>
</GridLayout>
</StackLayout>
</ScrollView>
</Page>
</template> </template>
<script> <script>
@ -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,145 +92,182 @@ export default {
return { return {
viewIsScrolled: false, viewIsScrolled: false,
appTheme: "Light", appTheme: "Light",
appLanguage: "English",
backupProgress: 0, backupProgress: 0,
backupInProgress: false, backupInProgress: false,
} }
}, },
computed: { computed: {
...mapState([ ...mapState( [
"icon", "icon",
"recipes", "recipes",
"userCategories", "userCategories",
"userYieldUnits", "userYieldUnits",
"mealPlans", "mealPlans",
"currentComponent", "currentComponent",
]), "language",
] ),
}, },
methods: { methods: {
...mapActions([ ...mapActions( [
"setCurrentComponentAction", "setCurrentComponentAction",
"importCategoriesAction", "importCategoriesAction",
"importYieldUnitsAction", "importYieldUnitsAction",
"importRecipesAction", "importRecipesAction",
"importMealPlansAction", "importMealPlansAction",
]), ] ),
onPageLoad() { onPageLoad( args ) {
this.setCurrentComponentAction("Settings") const page = args.object;
page.bindingContext = new Observable();
this.setCurrentComponentAction( "Settings" )
}, },
// HELPERS // HELPERS
showDrawer() { showDrawer() {
utils.showDrawer() utils.showDrawer()
}, },
onScroll(args) { onScroll( args ) {
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, {
props: { props: {
title: "Theme", title: "Theme",
list: ["Light", "Dark"], list: [ "Light", "Dark" ],
height: "96", height: "96",
}, },
}).then((action) => { } ).then( ( action ) => {
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 ) {
this.appTheme = action this.appTheme = action
ApplicationSettings.setString("appTheme", action) ApplicationSettings.setString( "appTheme", action )
setTimeout((e) => utils.restartApp(), 250) setTimeout( ( e ) => utils.restartApp(), 250 )
} }
}) } )
} }
}) } )
}, },
// EXPORT HANDLERS // EXPORT HANDLERS
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
) )
} }
}, },
exportBackup() { exportBackup() {
this.exportFiles("create") this.exportFiles( "create" )
let date = new Date() let date = new Date()
let formattedDate = let formattedDate =
date.getFullYear() + date.getFullYear() +
"-" + "-" +
("0" + (date.getMonth() + 1)).slice(-2) + ( "0" + ( date.getMonth() + 1 ) ).slice( -2 ) +
"-" + "-" +
("0" + date.getDate()).slice(-2) + ( "0" + date.getDate() ).slice( -2 ) +
"_" + "_" +
("0" + date.getHours()).slice(-2) + ( "0" + date.getHours() ).slice( -2 ) +
("0" + date.getMinutes()).slice(-2) + ( "0" + date.getMinutes() ).slice( -2 ) +
("0" + date.getSeconds()).slice(-2) ( "0" + date.getSeconds() ).slice( -2 )
const sdDownloadPath = Folder.fromPath( const sdDownloadPath = Folder.fromPath(
android.os.Environment.getExternalStorageDirectory().getAbsolutePath() android.os.Environment.getExternalStorageDirectory().getAbsolutePath()
).getFolder("Download").path ).getFolder( "Download" ).path
let fromPath = path.join(knownFolders.documents().path, "EnRecipes") let fromPath = path.join( knownFolders.documents().path, "EnRecipes" )
let destPath = path.join( let destPath = path.join(
sdDownloadPath, sdDownloadPath,
`EnRecipes-Backup_${formattedDate}.zip` `EnRecipes-Backup_${formattedDate}.zip`
) )
this.backupInProgress = true this.backupInProgress = true
Zip.zip({ Zip.zip( {
directory: fromPath, directory: fromPath,
archive: destPath, archive: destPath,
onProgress: (progress) => { onProgress: ( progress ) => {
this.backupProgress = progress this.backupProgress = progress
}, },
}).then((success) => { } ).then( ( success ) => {
Toast.makeText( Toast.makeText(
"Backup file successfully saved to Download folder", "Backup file successfully saved to Download folder",
"long" "long"
).show() ).show()
this.exportFiles("delete") this.exportFiles( "delete" )
}) } )
}, },
exportFiles(option) { exportFiles( option ) {
const folder = path.join(knownFolders.documents().path, "EnRecipes") const folder = path.join( knownFolders.documents().path, "EnRecipes" )
const EnRecipesFile = File.fromPath(path.join(folder, "recipes.json")) const EnRecipesFile = File.fromPath( path.join( folder, "recipes.json" ) )
let userCategoriesFile, userYieldUnitsFile, mealPlansFile let userCategoriesFile, userYieldUnitsFile, mealPlansFile
if (this.userCategories.length) { if ( this.userCategories.length ) {
userCategoriesFile = File.fromPath( userCategoriesFile = File.fromPath(
path.join(folder, "userCategories.json") path.join( folder, "userCategories.json" )
) )
} }
if (this.userYieldUnits.length) { if ( this.userYieldUnits.length ) {
userYieldUnitsFile = File.fromPath( userYieldUnitsFile = File.fromPath(
path.join(folder, "userYieldUnits.json") path.join( folder, "userYieldUnits.json" )
) )
} }
if (this.mealPlans.length) { if ( this.mealPlans.length ) {
mealPlansFile = File.fromPath(path.join(folder, "mealPlans.json")) mealPlansFile = File.fromPath( path.join( folder, "mealPlans.json" ) )
} }
switch (option) { switch ( option ) {
case "create": case "create":
this.writeDataToFile(EnRecipesFile, this.recipes) this.writeDataToFile( EnRecipesFile, this.recipes )
this.userCategories.length && this.userCategories.length &&
this.writeDataToFile(userCategoriesFile, this.userCategories) this.writeDataToFile( userCategoriesFile, this.userCategories )
this.userYieldUnits.length && this.userYieldUnits.length &&
this.writeDataToFile(userYieldUnitsFile, this.userYieldUnits) this.writeDataToFile( userYieldUnitsFile, this.userYieldUnits )
this.mealPlans.length && this.mealPlans.length &&
this.writeDataToFile(mealPlansFile, this.mealPlans) this.writeDataToFile( mealPlansFile, this.mealPlans )
break break
case "delete": case "delete":
EnRecipesFile.remove() EnRecipesFile.remove()
@ -245,144 +279,159 @@ export default {
break break
} }
}, },
writeDataToFile(file, data) { writeDataToFile( file, data ) {
file.writeText(JSON.stringify(data)) file.writeText( JSON.stringify( data ) )
}, },
// IMPORT HANDLERS // IMPORT HANDLERS
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
) )
}, },
openFilePicker() { openFilePicker() {
Filepicker.create({ Filepicker.create( {
mode: "single", mode: "single",
extensions: ["zip"], extensions: [ "zip" ],
}) } )
.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 )
}) } )
}, },
importDataToDB(data, db, zipPath) { importDataToDB( data, db, zipPath ) {
switch (db) { switch ( db ) {
case "EnRecipesDB": case "EnRecipesDB":
this.importImages(zipPath) this.importImages( zipPath )
this.importRecipesAction(data) this.importRecipesAction( data )
break break
case "userCategoriesDB": case "userCategoriesDB":
this.importCategoriesAction(data) this.importCategoriesAction( data )
break break
case "userYieldUnitsDB": case "userYieldUnitsDB":
this.importYieldUnitsAction(data) this.importYieldUnitsAction( data )
break break
case "mealPlansDB": case "mealPlansDB":
this.importMealPlansAction(data) this.importMealPlansAction( data )
break break
default: default:
break break
} }
}, },
isFileDataValid(file) { isFileDataValid( file ) {
file.forEach((file, i) => { file.forEach( ( file, i ) => {
if (File.exists(file.path)) { if ( File.exists( file.path ) ) {
File.fromPath(file.path) File.fromPath( file.path )
.readText() .readText()
.then((data) => { .then( ( data ) => {
Array.isArray(JSON.parse(data)) && Array.isArray( JSON.parse( data ) ) &&
this.importDataToDB(JSON.parse(data), file.db, file.zipPath) this.importDataToDB( JSON.parse( data ), file.db, file.zipPath )
}) } )
} }
}) } )
}, },
validateZipContent(zipPath) { validateZipContent( zipPath ) {
Zip.unzip({ Zip.unzip( {
archive: zipPath, archive: zipPath,
overwrite: true, overwrite: true,
}).then((extractedFolderPath) => { } ).then( ( extractedFolderPath ) => {
let cacheFolderPath = extractedFolderPath + "/EnRecipes" let cacheFolderPath = extractedFolderPath + "/EnRecipes"
const EnRecipesFilePath = cacheFolderPath + "/recipes.json" const EnRecipesFilePath = cacheFolderPath + "/recipes.json"
const userCategoriesFilePath = cacheFolderPath + "/userCategories.json" const userCategoriesFilePath = cacheFolderPath + "/userCategories.json"
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"
} else { },
Folder.fromPath(extractedFolderPath).remove() {
zipPath,
path: userYieldUnitsFilePath,
db: "userYieldUnitsDB"
},
{
zipPath,
path: mealPlansFilePath,
db: "mealPlansDB"
},
] )
}
else {
Folder.fromPath( extractedFolderPath ).remove()
Toast.makeText( Toast.makeText(
"Import failed. Backup file is incorrect or corrupt", "Import failed. Backup file is incorrect or corrupt",
"long" "long"
).show() ).show()
} }
if (Folder.exists(cacheFolderPath + "/Images")) { if ( Folder.exists( cacheFolderPath + "/Images" ) ) {
this.importImages(cacheFolderPath + "/Images") this.importImages( cacheFolderPath + "/Images" )
} }
}) } )
}, },
importImages(sourcePath) { importImages( sourcePath ) {
let dest = knownFolders.documents().path let dest = knownFolders.documents().path
Zip.unzip({ Zip.unzip( {
archive: sourcePath, archive: sourcePath,
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()
}) } )
}, },
// PERMISSIONS HANDLER // PERMISSIONS HANDLER
permissionCheck(confirmation, description, action) { permissionCheck( confirmation, description, action ) {
if (!ApplicationSettings.getBoolean("storagePermissionAsked", false)) { if ( !ApplicationSettings.getBoolean( "storagePermissionAsked", false ) ) {
confirmation(description).then((e) => { confirmation( description ).then( ( e ) => {
if (e) { if ( e ) {
Permissions.request("photo").then((res) => { Permissions.request( "photo" ).then( ( res ) => {
let status = res[Object.keys(res)[0]] let status = res[ Object.keys( res )[ 0 ] ]
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 { }
Permissions.check("photo").then((res) => { else {
let status = res[Object.keys(res)[0]] Permissions.check( "photo" ).then( ( res ) => {
if (status !== "authorized") { let status = res[ Object.keys( res )[ 0 ] ]
confirmation(description).then((e) => { if ( status !== "authorized" ) {
confirmation( description ).then( ( e ) => {
e && utils.openAppSettingsPage() e && utils.openAppSettingsPage()
}) } )
} else action() }
}) else action()
} )
} }
}, },
permissionConfirmation(description) { permissionConfirmation( description ) {
return this.$showModal(ConfirmDialog, { return this.$showModal( ConfirmDialog, {
props: { props: {
title: "Grant permission", title: "Grant permission",
description, description,
cancelButtonText: "NOT NOW", cancelButtonText: "NOT NOW",
okButtonText: "CONTINUE", okButtonText: "CONTINUE",
}, },
}) } )
}, },
}, },
mounted() { mounted() {
this.appTheme = ApplicationSettings.getString("appTheme", "Light") this.appTheme = ApplicationSettings.getString( "appTheme", "Light" )
this.appLanguage = ApplicationSettings.getString( "appLanguage", localize( "System default" ) )
}, },
} }
</script> </script>

File diff suppressed because it is too large Load diff

View file

@ -1,67 +1,44 @@
<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" </StackLayout>
:key="index" </ScrollView>
class="actionItem" <GridLayout rows="auto" columns="auto, *, auto" class="actionsContainer">
variant="text" <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)" />
:text="item" </GridLayout>
@loaded="onLabelLoaded" </StackLayout>
@tap="tapAction(item)" </Page>
/>
</StackLayout>
</ScrollView>
<GridLayout rows="auto" columns="auto, *, auto" class="actionsContainer">
<MDButton
:rippleColor="rippleColor"
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>
</StackLayout>
</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: {
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)"
}, },
screenHeight() { screenHeight() {
return Math.round(Screen.mainScreen.heightDIPs) return Math.round( Screen.mainScreen.heightDIPs )
}, },
}, },
methods: { methods: {
tapAction(item) { tapAction( item ) {
this.$modal.close(item) this.$modal.close( item )
}, },
onLabelLoaded(args) { onLabelLoaded( args ) {
args.object.android.setGravity(16) args.object.android.setGravity( 16 )
}, },
}, },
} }

View file

@ -1,62 +1,32 @@
<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>
<ScrollView width="100%" :height="height ? height : screenHeight - 320">
<StackLayout>
<MDButton
v-for="(recipe, index) in filteredRecipes"
: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>
</ScrollView>
<GridLayout rows="auto" columns="auto, *, auto" class="actionsContainer">
<MDButton
:rippleColor="rippleColor"
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>
</StackLayout> </StackLayout>
</Page> <ScrollView width="100%" :height="height ? height : screenHeight - 320">
<StackLayout>
<MDButton v-for="(recipe, index) in filteredRecipes" :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.' | L" textAlignment="center" textWrap="true" />
</StackLayout>
</ScrollView>
<GridLayout rows="auto" columns="auto, *, auto" class="actionsContainer">
<MDButton :rippleColor="rippleColor" variant="text" v-if="action" col="0" class="action orkm pull-left" :text="`${action}` | L" @tap="$modal.close(action)" />
<MDButton :rippleColor="rippleColor" variant="text" col="2" class="action orkm pull-right" :text="'CANCEL' | L" @tap="$modal.close(false)" />
</GridLayout>
</StackLayout>
</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() {
return { return {
searchTerm: "", searchTerm: "",
@ -67,30 +37,26 @@ 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: {
tapAction(recipe) { tapAction( recipe ) {
this.$modal.close(recipe.id) this.$modal.close( recipe.id )
}, },
onLabelLoaded(args) { onLabelLoaded( args ) {
args.object.android.setGravity(16) args.object.android.setGravity( 16 )
}, },
}, },
} }

View file

@ -1,47 +1,31 @@
<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" <GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
class="dialogDescription" <MDButton :rippleColor="rippleColor" variant="text" col="1" class="action orkm" :text="`${cancelButtonText}` | L" @tap="$modal.close(false)" />
:text="description" <MDButton :rippleColor="rippleColor" variant="text" col="2" class="action orkm" :text="`${okButtonText}` | L" @tap="$modal.close(true)" />
textWrap="true" </GridLayout>
/> </StackLayout>
<GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer"> </Page>
<MDButton
:rippleColor="rippleColor"
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>
</StackLayout>
</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: {
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(134,142,150,0.2)" :
: "rgba(206,212,218,0.1)" "rgba(206,212,218,0.1)"
}, },
}, },
} }

View file

@ -1,138 +1,73 @@
<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>
<GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
<MDButton
:rippleColor="rippleColor"
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>
</StackLayout> </StackLayout>
</Page> <GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
<MDButton :rippleColor="rippleColor" variant="text" col="1" class="action orkm" :text="'CANCEL' | L" @tap="$modal.close(false)" />
<MDButton :rippleColor="rippleColor" variant="text" col="2" class="action orkm" :text="`${action}` | L" @tap="$modal.close(selectedTime)" />
</GridLayout>
</StackLayout>
</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
}, },
}, },
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,41 +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 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>
<GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
<MDButton
:rippleColor="rippleColor"
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>
</StackLayout> </StackLayout>
</Page> <GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
<MDButton :rippleColor="rippleColor" variant="text" col="1" class="action orkm" :text="'CANCEL' | L" @tap="$modal.close(false)" />
<MDButton :rippleColor="rippleColor" variant="text" col="2" class="action orkm" :text="`${action}` | L" @tap="$modal.close(category)" />
</GridLayout>
</StackLayout>
</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() {
return { return {
category: null, category: null,
@ -46,20 +35,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)"
}, },
}, },
methods: { methods: {
focusField(args) { focusField( args ) {
args.object.focus() args.object.focus()
setTimeout((e) => Utils.ad.showSoftInput(args.object.android), 1) setTimeout( ( e ) => Utils.ad.showSoftInput( args.object.android ), 1 )
}, },
}, },
mounted() { mounted() {
if (this.text) { if ( this.text ) {
this.category = this.text this.category = localize( this.text )
} }
}, },
} }

View file

@ -1,45 +1,31 @@
<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" </GridLayout>
row="0" </StackLayout>
class="actionIcon" </Page>
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>
</StackLayout>
</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: {
...mapState(["icon"]), ...mapState( [ "icon" ] ),
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)"
}, },
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) => {
@ -261,9 +293,9 @@ export default new Vuex.Store({
importMealPlans(state, mealPlans) { importMealPlans(state, mealPlans) {
let newMealPlans = mealPlans.filter( let newMealPlans = mealPlans.filter(
(e) => (e) =>
!state.mealPlans.some( !state.mealPlans.some(
(f) => f.title === e.title && f.startDate === e.startDate (f) => f.title === e.title && f.startDate === e.startDate
) )
) )
state.mealPlans = [...state.mealPlans, ...newMealPlans] state.mealPlans = [...state.mealPlans, ...newMealPlans]
mealPlansDB.updateDocument("mealPlans", { mealPlansDB.updateDocument("mealPlans", {
@ -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