v2.0.0 50% ready

This commit is contained in:
vishnuraghavb 2021-06-15 16:34:42 +05:30
parent b26a38d4ec
commit 1f9d7c1232
155 changed files with 6237 additions and 4059 deletions

View file

@ -6,4 +6,4 @@ Here are some important resources:
- The [roadmap](https://github.com/vishnuraghavb/EnRecipes/projects/1) will tell you whats the future of EnRecipes. Your feedback and suggestions are very important to make EnRecipes the best. If you have an idea to improve EnRecipes, [do let me know](https://github.com/vishnuraghavb/EnRecipes#having-issues-suggestions-and-feedback). I'm always open to ideas ;) - The [roadmap](https://github.com/vishnuraghavb/EnRecipes/projects/1) will tell you whats the future of EnRecipes. Your feedback and suggestions are very important to make EnRecipes the best. If you have an idea to improve EnRecipes, [do let me know](https://github.com/vishnuraghavb/EnRecipes#having-issues-suggestions-and-feedback). I'm always open to ideas ;)
- You can help [translate EnRecipes using Weblate](https://hosted.weblate.org/engage/enrecipes/) into the language of your choice. See [translation instructions](https://github.com/vishnuraghavb/EnRecipes/wiki/Translation-Instructions) in the wiki for more information. - You can help [translate EnRecipes using Weblate](https://hosted.weblate.org/engage/enrecipes/) into the language of your choice. See [translation instructions](https://github.com/vishnuraghavb/EnRecipes/wiki/Translation-Instructions) in the wiki for more information.
- Bugs, suggestions or feedback? You can [create an issue here](https://github.com/vishnuraghavb/EnRecipes/issues) or [join the Telegram group](http://t.me/enrecipes)(quicker replies) or contact me at apps@vishnuraghav.com - Bugs, suggestions or feedback? You can [create an issue here](https://github.com/vishnuraghavb/EnRecipes/issues) or [join the Telegram group](http://t.me/enrecipes) (quicker replies) or contact me at apps@vishnuraghav.com

View file

@ -6,7 +6,7 @@
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><img src="https://img.shields.io/badge/license-GPL%20v3-blue"></a> <a href="https://github.com/vishnuraghavb/EnRecipes/releases" alt="Release version"><img src="https://img.shields.io/github/v/release/vishnuraghavb/EnRecipes?color=ff5200"/></a> <a href="https://hosted.weblate.org/engage/enrecipes/"> <a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><img src="https://img.shields.io/badge/license-GPL%20v3-blue"></a> <a href="https://github.com/vishnuraghavb/EnRecipes/releases" alt="Release version"><img src="https://img.shields.io/github/v/release/vishnuraghavb/EnRecipes?color=ff5200"/></a> <a href="https://hosted.weblate.org/engage/enrecipes/">
<img src="https://hosted.weblate.org/widgets/enrecipes/-/app-translations/svg-badge.svg" alt="Translation status" /></a> <img src="https://hosted.weblate.org/widgets/enrecipes/-/app-translations/svg-badge.svg" alt="Translation status" /></a>
<p align="center"> <p align="center">
<a href="https://play.google.com/store/apps/details?id=com.vishnuraghav.enrecipes"><img src="assets/Images/google-play-badge.png" height="80"/></a><a href="https://apt.izzysoft.de/fdroid/index/apk/com.vishnuraghav.enrecipes"><img src="assets/Images/IzzyOnDroid.png" height="80"/></a><br>You can also get the <a href="https://github.com/vishnuraghavb/EnRecipes/releases/latest">latest release on GitHub</a> <a href="https://play.google.com/store/apps/details?id=com.vishnuraghav.EnRecipes"><img src="assets/Images/google-play-badge.png" height="80"/></a><a href="https://apt.izzysoft.de/fdroid/index/apk/com.vishnuraghav.EnRecipes"><img src="assets/Images/IzzyOnDroid.png" height="80"/></a><br>You can also get the <a href="https://github.com/vishnuraghavb/EnRecipes/releases/latest">latest release on GitHub</a>
</p> </p>
<h2 align="center">Enjoying EnRecipes?</h2> <h2 align="center">Enjoying EnRecipes?</h2>
<p align="center">Please consider making a small donation to help fund the project. Developing an application, especially one that is open source and completely free, takes a lot of time and effort. <p align="center">Please consider making a small donation to help fund the project. Developing an application, especially one that is open source and completely free, takes a lot of time and effort.
@ -28,16 +28,17 @@
- 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.
- Shake device to view random recipe - Shake your device to view a random recipe
- Create meal plans - Create meal plans
- Import/Export recipes - Set cooking timers
- Light, Dark and Black themes - You can Import or Export your data
- Has Light, Dark and Black themes
## Highlights ## Highlights
- 100% free and open-source - 100% free and open-source
- Private by Design - Private by Design
- No permissions required - No special permissions required
- No annoying ads or pop-ups - No annoying ads or pop-ups
**Languages being translated**: **Languages being translated**:

View file

@ -1,3 +1,4 @@
// Colours
$gray0: #f8f9fa; $gray0: #f8f9fa;
$gray1: #f1f3f5; $gray1: #f1f3f5;
$gray2: #e9ecef; $gray2: #e9ecef;
@ -11,21 +12,29 @@ $gray9: #212529;
$gray10: #000000; $gray10: #000000;
$orange: #ff5200; $orange: #ff5200;
// FontSizes based on Minor Third
$t1: 25;
$t2: 21;
$t3: 17;
$t4: 14;
$t5: 12; // Base size
$t6: 10;
Page { Page {
font-family: 'Inter-Medium'; font-family: 'Inter-Medium', sans-serif;
font-size: 14; font-size: $t4;
} }
.ico { .ico {
font-family: 'enrecipes'; font-family: 'enrecipes';
font-size: 24; font-size: 24;
vertical-alignment: center; vertical-alignment: center;
&.sm { &.sm {
font-size: 16; font-size: $t3;
opacity: 0.5; opacity: 0.5;
} }
} }
.tb { .tb {
font-family: 'Inter-Bold'; font-family: 'Inter-Bold', sans-serif;
} }
.tac { .tac {
text-align: center; text-align: center;
@ -36,7 +45,7 @@ Page {
.pageTitle { .pageTitle {
@extend .tb; @extend .tb;
@extend .tw; @extend .tw;
font-size: 25; font-size: $t1;
padding: 16 16 24; padding: 16 16 24;
} }
.Light { .Light {
@ -60,7 +69,6 @@ Page {
background: $gray0; background: $gray0;
} }
.fieldLabel, .fieldLabel,
.dayName,
.sub { .sub {
color: $gray6; color: $gray6;
} }
@ -98,7 +106,6 @@ Page {
background: $gray8; background: $gray8;
} }
.fieldLabel, .fieldLabel,
.dayName,
.sub { .sub {
color: $gray5; color: $gray5;
} }
@ -136,7 +143,6 @@ Page {
background: $gray9; background: $gray9;
} }
.fieldLabel, .fieldLabel,
.dayName,
.sub { .sub {
color: $gray6; color: $gray6;
} }
@ -169,14 +175,13 @@ TextView {
line-height: 4; line-height: 4;
} }
#searchBar { #searchBar {
padding-left: 0; padding: 13 12;
margin: 0;
} }
.inputField { .inputField {
margin-bottom: 24; margin-bottom: 24;
} }
.fieldLabel { .fieldLabel {
font-size: 12; font-size: $t5;
} }
.progressContainer { .progressContainer {
width: 100%; width: 100%;
@ -198,25 +203,28 @@ button {
border-radius: 12; border-radius: 12;
min-width: 0; min-width: 0;
min-height: 0; min-height: 0;
&:active {
@extend .fade;
}
&.ico { &.ico {
width: 48; width: 48;
height: 48; height: 48;
padding: 0; padding: 0;
margin: 0; margin: 0;
&:active {
@extend .fade;
}
} }
&.text { &.text {
@extend .tb; @extend .tb;
color: $orange; color: $orange;
&:active {
@extend .fade;
}
} }
&.big { &.big {
margin-top: 8; margin-top: 8;
padding: 16 0; padding: 16 0;
} }
&.sm { &.sm {
font-size: 12; font-size: $t5;
padding: 12; padding: 12;
} }
&.min { &.min {
@ -224,10 +232,16 @@ button {
height: 40; height: 40;
vertical-alignment: center; vertical-alignment: center;
} }
&.fb:active {
@extend .fade;
}
&.rate { &.rate {
margin: 0 4 0 0; margin: 0 4 0 0;
width: 32; width: 32;
height: 32; height: 32;
&:active {
@extend .fade;
}
} }
} }
ActivityIndicator { ActivityIndicator {
@ -246,14 +260,15 @@ ActivityIndicator {
.value { .value {
padding: 0 0 0 8; padding: 0 0 0 8;
vertical-alignment: center; vertical-alignment: center;
&.rtl { &.r {
padding: 0 8 0 0; padding: 0 8 0 0;
transform: scaleX(-1);
} }
} }
&.select { }
.select {
color: $orange; color: $orange;
@extend .hl; @extend .hl;
}
} }
.emptyState { .emptyState {
padding: 16 16 8; padding: 16 16 8;
@ -262,7 +277,7 @@ ActivityIndicator {
} }
.title { .title {
@extend .tb; @extend .tb;
font-size: 17; font-size: $t3;
} }
} }
@ -272,22 +287,18 @@ ActivityIndicator {
padding: 8 16; padding: 8 16;
.recipeInfo { .recipeInfo {
vertical-alignment: center; vertical-alignment: center;
padding: 0 0 4 8; padding: 0 8 4;
&.rtl {
transform: none;
padding: 0 8 4 0;
}
} }
.title { .title {
padding: 0 0 4; padding: 0 0 4;
} }
.attr { }
font-size: 10; .attrs {
padding: 0 6 1 2; orientation: horizontal;
&.rtl { }
padding: 0 2 1 6; .attr {
} font-size: $t6;
} padding: 1 4;
} }
.simple .recipeInfo { .simple .recipeInfo {
padding: 8 0; padding: 8 0;
@ -343,20 +354,24 @@ ActivityIndicator {
.group-info { .group-info {
padding: 16 16 16 72; padding: 16 16 16 72;
line-height: 4; line-height: 4;
&.r {
padding: 16 72 16 16;
}
} }
.options { .options {
.option { .option {
vertical-align: center; vertical-align: center;
padding: 14 12; padding: 14 16;
.ico { .ico {
margin: 0 24 0 12; width: 40;
text-align: center;
} }
.info, .info {
.sub { padding: 0 16;
@extend .tw; @extend .tw;
} }
.sub { .sub {
font-size: 12; font-size: $t5;
} }
} }
} }
@ -371,7 +386,7 @@ ActivityIndicator {
horizontal-alignment: center; horizontal-alignment: center;
} }
.name { .name {
font-size: 21; font-size: $t2;
} }
.info { .info {
padding: 8 16 24; padding: 8 16 24;
@ -394,9 +409,8 @@ ActivityIndicator {
} }
.attribute { .attribute {
margin: 8 16; margin: 8 16;
.title { .sub {
margin-right: 8; font-size: $t5;
font-size: 12;
} }
.value { .value {
@extend .tb; @extend .tb;
@ -416,7 +430,7 @@ ActivityIndicator {
padding: 0 16; padding: 0 16;
.count { .count {
@extend .tb; @extend .tb;
font-size: 17; font-size: $t3;
} }
.value { .value {
@extend .tw; @extend .tw;
@ -443,7 +457,7 @@ ActivityIndicator {
} }
.dateInfo { .dateInfo {
padding: 32 16 16; padding: 32 16 16;
font-size: 12; font-size: $t5;
line-height: 4; line-height: 4;
} }
@ -459,10 +473,12 @@ ActivityIndicator {
@extend .tb; @extend .tb;
@extend .tw; @extend .tw;
vertical-align: center; vertical-align: center;
margin: 0 12;
line-height: 4; line-height: 4;
} }
.msg { .msg {
padding: 14 16; padding: 14 16;
margin: 0;
} }
.fab { .fab {
margin-left: 8; margin-left: 8;
@ -471,21 +487,21 @@ ActivityIndicator {
margin: 8 8 0; margin: 8 8 0;
} }
} }
.sidebar {
margin-bottom: 0;
}
.toolbar { .toolbar {
z-index: 4; z-index: 4;
padding: 4; padding: 4;
margin: 0 0 52; margin: 0 0 52;
horizontal-alignment: left;
.tool { .tool {
padding: 0 12; padding: 0 8;
label { label {
vertical-alignment: center; vertical-alignment: center;
} }
.value,
.ico { .ico {
padding: 0 8 0 0; padding: 0 4;
&.rtl {
padding: 0 0 0 8;
}
} }
} }
} }
@ -499,7 +515,7 @@ ActivityIndicator {
.sectionTitle { .sectionTitle {
@extend .tb; @extend .tb;
@extend .tw; @extend .tw;
font-size: 21; font-size: $t2;
padding: 0; padding: 0;
margin: 32 0 16; margin: 32 0 16;
} }
@ -509,49 +525,51 @@ ActivityIndicator {
margin: 0; margin: 0;
} }
.countdown { .countdown {
font-size: 17; font-size: $t3;
color: $orange; color: $orange;
} }
// ----------------------------- // -----------------------------
// MealPlanner // MealPlanner
.calendar { .monthSwitcher {
padding: 0 8; padding: 0 16;
.navBtn { .month {
margin: 0;
}
.monthName {
text-align: center;
vertical-alignment: center; vertical-alignment: center;
font-size: 17;
}
.dayName {
margin: 8 0;
font-size: 12;
text-align: center; text-align: center;
} font-size: $t3;
.day {
border-radius: 12;
}
.hasPlans {
color: $orange;
}
.activeDay {
@extend .hl;
} }
} }
.dayPlan { .calendar {
padding: 16 16 80; padding: 0 16;
width: 100%; .dayName {
.periodLabel { vertical-alignment: center;
font-size: 17; text-align: center;
text-transform: capitalize; font-size: $t5;
vertical-align: center;
} }
.recipeTitle { .accent.sub {
@extend .tw; color: rgba($orange, 0.5);
padding: 16 8; }
line-height: 4; }
.plans {
padding: 8 16 80;
width: 100%;
.date {
font-size: $t2;
padding: 16 0;
}
.plan {
padding: 8 0;
}
.meal {
font-size: $t3;
padding: 8 0;
}
.planContent {
min-height: 48;
padding: 8;
}
.attr {
padding: 0;
} }
} }
@ -566,7 +584,7 @@ ActivityIndicator {
@extend .tb; @extend .tb;
@extend .tw; @extend .tw;
padding: 16; padding: 16;
font-size: 21; font-size: $t2;
} }
.input { .input {
padding: 0 16 8; padding: 0 16 8;
@ -581,13 +599,12 @@ ActivityIndicator {
margin: 16 0; margin: 16 0;
} }
.listItem { .listItem {
@extend .tw;
letter-spacing: 0; letter-spacing: 0;
text-transform: none; text-transform: none;
line-height: 4; line-height: 4;
padding: 13 16; padding: 13 16;
margin: 0; margin: 0;
background: transparent; background-color: transparent;
} }
.shareItem { .shareItem {
border-radius: 12; border-radius: 12;
@ -649,10 +666,10 @@ ActivityIndicator {
// ----------------------------- // -----------------------------
// Helpers // Helpers
.rtl { .f {
transform: scaleX(-1); transform: scaleX(-1);
} }
.clickable { .accent {
color: $orange; color: $orange;
} }
.hal { .hal {

View file

@ -1,53 +1,55 @@
<template> <template>
<Page @loaded="onPageLoad" actionBarHidden="true"> <Page @loaded="pgLoad" @unloaded="pgUnload" actionBarHidden="true">
<GridLayout rows="*, auto" columns="*"> <GridLayout rows="*, auto" columns="*">
<ScrollView <ScrollView
@scroll="onScroll($event)" @scroll="svScroll($event)"
rowSpan="2" rowSpan="2"
scrollBarIndicatorVisible="false" scrollBarIndicatorVisible="false"
> >
<StackLayout> <StackLayout>
<GridLayout rows="auto" columns="*, auto, 8"> <RGridLayout :rtl="RTL" rows="auto" columns="*, auto, 12">
<Label class="pageTitle" :text="'timer' | L" /> <RLabel class="pageTitle" :text="'timer' | L" />
<Button <Button col="1" class="ico" :text="icon.cog" @tap="navigateTo" />
col="1" </RGridLayout>
class="ico"
:text="icon.cog"
@tap="$navigateTo(CTSettings)"
/>
</GridLayout>
<Timer <Timer
v-for="(timer, i) in activeTimers" v-for="timer in activeTimers"
:key="timer.id" :key="timer.id + key"
:timer="timer" :timer="timer"
:timerIndex="i"
:formattedTime="formattedTime" :formattedTime="formattedTime"
:removeTimer="removeTimer" :removeTimer="removeTimer"
:addToPreset="addToPreset"
:togglePause="togglePause" :togglePause="togglePause"
:fireTimer="fireTimer" :timerAlert="timerAlert"
:showToast="showToast"
/> />
<StackLayout class="listSpace"> </StackLayout> <StackLayout class="listSpace"> </StackLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
<GridLayout <GridLayout v-if="!activeTimers.length" rows="*, auto">
<StackLayout row="1" class="emptyState">
<RLabel class="title" :text="'ccwt' | L" />
<RLabel :text="'plsAdd' | L" />
</StackLayout>
</GridLayout>
<RGridLayout
:rtl="RTL"
row="1" row="1"
@loaded="onAppBarLoad" @loaded="abLoad"
class="appbar" class="appbar"
:hidden="showUndo" :hidden="showUndo"
columns="auto, *, auto" columns="auto, *, auto"
> >
<Button class="ico" :text="icon.back" @tap="$navigateBack()" /> <Button class="ico rtl" :text="icon.back" @tap="navigateBack" />
<Button class="ico fab" :text="icon.plus" @tap="addTimer" col="2" /> <Button class="ico fab" :text="icon.plus" @tap="addTimer" col="2" />
</GridLayout> </RGridLayout>
<SnackBar <SnackBar
:hidden="!showUndo || toast" :hidden="!showUndo || toast"
:count="countdown" :count="countdown"
:msg="snackMsg" :msg="snackMsg"
:undo="undoDel" :undo="undoDel"
:action="hideBar" :action="hideBar"
:onload="sbLoad"
/> />
<Toast :toast="toast" :action="hideBar" /> <Toast :onload="tbLoad" :toast="toast" :action="hideBar" />
</GridLayout> </GridLayout>
</Page> </Page>
</template> </template>
@ -58,13 +60,17 @@ import {
Observable, Observable,
CoreTypes, CoreTypes,
Application, Application,
ApplicationSettings,
AndroidApplication,
Utils, Utils,
Device, Device,
Frame,
} from "@nativescript/core"; } from "@nativescript/core";
import {
getNumber,
setNumber,
remove,
} from "@nativescript/core/application-settings";
import { mapState, mapActions } from "vuex"; import { mapState, mapActions } from "vuex";
import EnRecipes from "./EnRecipes.vue";
import Action from "./modals/Action.vue"; import Action from "./modals/Action.vue";
import CTSettings from "./settings/CTSettings.vue"; import CTSettings from "./settings/CTSettings.vue";
import TimePickerHMS from "./modals/TimePickerHMS.vue"; import TimePickerHMS from "./modals/TimePickerHMS.vue";
@ -72,13 +78,10 @@ import TimerReminder from "./modals/TimerReminder.vue";
import Timer from "./sub/Timer.vue"; import Timer from "./sub/Timer.vue";
import Toast from "./sub/Toast.vue"; import Toast from "./sub/Toast.vue";
import SnackBar from "./sub/SnackBar.vue"; import SnackBar from "./sub/SnackBar.vue";
import * as utils from "~/shared/utils"; import * as utils from "~/shared/utils";
import { EventBus } from "~/main"; import { EvtBus } from "~/main";
let undoTimer, let barTimer;
firingTimers = []; declare const com, android: any;
declare const com: any;
export default { export default {
components: { Timer, Toast, SnackBar }, components: { Timer, Toast, SnackBar },
props: ["recipeID"], props: ["recipeID"],
@ -86,43 +89,62 @@ export default {
return { return {
scrollPos: 1, scrollPos: 1,
appbar: null, appbar: null,
toastbar: null,
snackbar: null,
scrollView: null,
countdown: 5, countdown: 5,
snackMsg: null, snackMsg: null,
showUndo: false, showUndo: 0,
undo: false, undo: 0,
CTSettings: CTSettings,
toast: null, toast: null,
key: 99,
}; };
}, },
computed: { computed: {
...mapState([ ...mapState([
"icon", "icon",
"recipes", "recipes",
"currentComponent",
"timerSound", "timerSound",
"timerVibrate", "timerVibrate",
"timerDelay",
"timerPresets", "timerPresets",
"activeTimers", "activeTimers",
"FGService",
"RTL",
]), ]),
hasBackStack() {
return Frame.topmost().backStack.length;
},
}, },
methods: { methods: {
...mapActions([ ...mapActions([
"setComponent",
"addActiveTimer", "addActiveTimer",
"removeActiveTimer", "removeActiveTimer",
"clearTimerInterval", "clearTimerInterval",
"addTimerPreset", "addTimerPreset",
"updateActiveTimer", "updateActiveTimer",
"setFGService",
]), ]),
onPageLoad({ object }) { pgLoad({ object }) {
object.bindingContext = new Observable(); object.bindingContext = new Observable();
this.setComponent("CookingTimer"); if (this.activeTimers.filter((e: any) => e.done).length)
this.openReminder();
this.keepScreenOnCountUp();
setNumber("isTimer", 1);
}, },
onAppBarLoad({ object }) { pgUnload() {
utils.keepScreenOn(0);
},
abLoad({ object }) {
this.appbar = object; this.appbar = object;
}, },
onScroll(args) { tbLoad({ object }) {
this.toastbar = object;
},
sbLoad({ object }) {
this.snackbar = object;
},
svScroll(args) {
this.scrollView = args.object;
let scrollUp; let scrollUp;
let y = args.scrollY; let y = args.scrollY;
if (y) { if (y) {
@ -132,18 +154,19 @@ export default {
if (!scrollUp && ab == 0) { if (!scrollUp && ab == 0) {
this.appbar.animate({ this.appbar.animate({
translate: { x: 0, y: 64 }, translate: { x: 0, y: 64 },
duration: 250, duration: 200,
curve: CoreTypes.AnimationCurve.ease, curve: CoreTypes.AnimationCurve.ease,
}); });
} else if (scrollUp && ab == 64) { } else if (scrollUp && ab == 64) {
this.appbar.animate({ this.appbar.animate({
translate: { x: 0, y: 0 }, translate: { x: 0, y: 0 },
duration: 250, duration: 200,
curve: CoreTypes.AnimationCurve.ease, curve: CoreTypes.AnimationCurve.ease,
}); });
} }
} }
}, },
// HELPERS // HELPERS
getRecipeTitle(id) { getRecipeTitle(id) {
let recipe = this.recipes.filter((e) => e.id === id)[0]; let recipe = this.recipes.filter((e) => e.id === id)[0];
@ -168,81 +191,111 @@ export default {
}, },
// NOTIFICATION HANDLERS // NOTIFICATION HANDLERS
notifyTimers() { timerInfo() {
let activeCount = this.activeTimers.length; let activeCount = this.activeTimers.length;
let pausedCount = this.activeTimers.filter((e) => e.isPaused).length; let pausedCount = this.activeTimers.filter((e) => e.isPaused).length;
let ongoingCount = activeCount - pausedCount; let ongoingCount = activeCount - pausedCount;
console.log("notifying"); this.foregroundService(activeCount);
function show() {
utils.TimerNotif.show({ utils.TimerNotif.show({
bID: "bringToFront", bID: "info",
cID: "cti", cID: "cti",
cName: "Cooking Timer info", cName: "Cooking Timer info",
description: `${ongoingCount} ongoing, ${pausedCount} paused`, description: localize("oAP", ongoingCount + "", pausedCount),
nID: 777, nID: 6,
priority: -2, priority: -2,
sound: null, sound: null,
title: localize("timer"), title: localize("timer"),
}); });
if (activeCount <= 0) this.foregroundService(false); }
if (this.FGService)
setTimeout(() => this.activeTimers.length && show(), 250);
this.keepScreenOnCountUp();
utils.wakeLock(ongoingCount);
}, },
fireTimer(timer) { timerAlert() {
console.log("firing"); let title, description, bID;
let description = timer.recipeID let firedTimers = this.activeTimers.filter((e) => e.done);
? " - " + this.getRecipeTitle(timer.recipeID) let timer = firedTimers[0];
: ""; if (firedTimers.length > 1) {
let title = timer.label; title = localize("texp", firedTimers.length);
let time = this.formattedTime(timer.time); description = localize("ttv");
let bID = "timer" + timer.id; bID = "alerts";
} else if (firedTimers.length == 1) {
title =
timer.label +
(timer.recipeID ? " - " + this.getRecipeTitle(timer.recipeID) : "");
description = this.formattedTime(timer.time);
bID = "timer" + timer.id;
} else {
utils.TimerNotif.clear(7);
return;
}
utils.TimerNotif.show({ utils.TimerNotif.show({
actions: true, actions: 1,
bID, bID,
cID: "cta", cID: "cta",
cName: "Cooking Timer alerts", cName: "Cooking Timer alerts",
description: time, description,
nID: timer.id, multi: firedTimers.length > 1,
nID: 7,
priority: 1, priority: 1,
sound: this.timerSound.uri, sound: this.timerSound.uri,
title: title + description, title,
vibrate: this.timerVibrate, vibrate: this.timerVibrate,
}); });
if (firedTimers.length == 1) {
Application.android.registerBroadcastReceiver(bID, (ctx, intent) => { Application.android.registerBroadcastReceiver(bID, (ctx, intent) => {
let action = intent.getStringExtra("action"); EvtBus.$emit(bID, intent.getStringExtra("action"));
console.log(action, "firing"); Application.android.unregisterBroadcastReceiver(bID);
EventBus.$emit(bID, action);
}); });
firingTimers.push(timer); } else {
// if (firingTimers.length == 1) { Application.android.unregisterBroadcastReceiver(bID);
// this.$showModal(TimerReminder, { Application.android.registerBroadcastReceiver(bID, (ctx, intent) => {
// fullscreen: true, if (intent.getStringExtra("action") == "dismissAll") {
// props: { firedTimers.forEach((t) => this.removeTimer(t.id, 1));
// timers: firingTimers, Application.android.unregisterBroadcastReceiver(bID);
// stop: this.stopFiringTimers, }
// formattedTime: this.formattedTime, });
// }, }
// });
// }
}, },
stopFiringTimers() { openReminder() {
firingTimers.forEach((e) => utils.TimerNotif.clear(e.id)); this.clearTimerInterval();
firingTimers = []; this.$showModal(TimerReminder, {
fullscreen: true,
props: {
formattedTime: this.formattedTime,
removeTimer: this.removeTimer,
togglePause: this.togglePause,
timerAlert: this.timerAlert,
showToast: this.showToast,
}, },
openReminder() {}, }).then(() => {
foregroundService(bool) { this.clearTimerInterval();
this.key = Math.floor(Math.random() * 900) + 100;
});
},
foregroundService(n) {
const ctx = Utils.ad.getApplicationContext(); const ctx = Utils.ad.getApplicationContext();
const intent = new android.content.Intent( const intent = new android.content.Intent(
ctx, ctx,
com.tns.ForegroundService.class com.tns.ForegroundService.class
); );
if (bool) if (n && !this.FGService) {
parseInt(Device.sdkVersion) < 26 parseInt(Device.sdkVersion) < 26
? ctx.startService(intent) ? ctx.startService(intent)
: ctx.startForegroundService(intent); : ctx.startForegroundService(intent);
else ctx.stopService(intent); this.setFGService(1);
setNumber("FGService", 1);
} else if (!this.activeTimers.length) {
ctx.stopService(intent);
this.setFGService(0);
setNumber("FGService", 0);
}
}, },
// DATA HANDLERS // DATA HANDLERS
addTimer() { addTimer() {
this.foregroundService(true);
this.$showModal(TimePickerHMS, { this.$showModal(TimePickerHMS, {
props: { props: {
title: "ntmr", title: "ntmr",
@ -258,7 +311,7 @@ export default {
); );
this.$showModal(Action, { this.$showModal(Action, {
props: { props: {
title: "tmrPrsts", title: "prsts",
list, list,
}, },
}).then((preset) => { }).then((preset) => {
@ -266,128 +319,155 @@ export default {
let timer = JSON.parse( let timer = JSON.parse(
JSON.stringify(this.timerPresets[list.indexOf(preset)]) JSON.stringify(this.timerPresets[list.indexOf(preset)])
); );
timer.id = this.getRandomID(); timer.id = utils.getRandomID(1);
timer.recipeID = this.recipeID;
timer.timerInt = timer.isPaused = 0;
timer.preset = timer.mode = 1;
this.addActiveTimer({ this.addActiveTimer({
timer, timer,
index: this.activeTimers.length, i: this.activeTimers.length,
}); });
this.notifyTimers(); this.timerInfo();
} }
}); });
} else { } else {
if (res.time != "00:00:00") { let mode = res.time != "00:00:00" ? 1 : 0;
this.addActiveTimer({ this.addActiveTimer({
timer: { timer: {
id: this.getRandomID(), id: utils.getRandomID(1),
label: res.label, label: res.label,
recipeID: this.recipeID, recipeID: this.recipeID,
time: res.time, time: res.time,
timerInterval: null, timerInt: 0,
isPaused: false, isPaused: 0,
preset: 0, preset: 0,
done: 0,
mode,
}, },
index: this.activeTimers.length, i: this.activeTimers.length,
}); });
this.notifyTimers(); this.timerInfo();
}
} }
} }
}); });
}, },
removeTimer(id, index, noUndo) { removeTimer(id, noUndo) {
let temp = this.activeTimers[index]; let i = this.activeTimers.findIndex((e) => e.id == id);
this.removeActiveTimer(index); let temp = this.activeTimers[i];
utils.TimerNotif.clear(id); clearInterval(temp.timerInt);
temp.timerInt = 0;
this.removeActiveTimer(i);
let secs = [getNumber(`${temp.id}c`, 0), getNumber(`${temp.id}d`, 0)];
function removeSettings() {
remove(`${temp.id}c`);
remove(`${temp.id}d`);
}
removeSettings();
if (!noUndo) { if (!noUndo) {
this.showUndoBar("tmrClr") this.showUndoBar("tmrRm")
.then(() => { .then(() => {
this.foregroundService(true); setNumber(`${temp.id}c`, secs[0]),
setNumber(`${temp.id}d`, secs[1]),
this.addActiveTimer({ this.addActiveTimer({
timer: temp, timer: temp,
index, i,
}); });
this.notifyTimers(); this.timerInfo();
}) })
.catch(() => { .catch(() => removeSettings());
ApplicationSettings.remove(`${temp.id}progress`);
});
} }
this.notifyTimers(); this.timerAlert();
this.timerInfo();
}, },
addToPreset(timer) { togglePause(timer, n) {
timer = JSON.parse(JSON.stringify(timer)); timer.isPaused =
timer.recipeID = timer.timerInterval = null; typeof n === "number" ? n : (!timer.isPaused as boolean | 0);
timer.preset = 1;
this.addTimerPreset(timer);
this.showToast("aTPrst");
},
togglePause(timer, bool) {
if (typeof bool === "boolean") timer.isPaused = bool;
else timer.isPaused = !timer.isPaused;
this.updateActiveTimer(timer); this.updateActiveTimer(timer);
this.notifyTimers(); n ? 0 : this.timerInfo();
}, },
showToast(data) { showToast(data) {
this.animateBar(this.snackbar, 0);
this.animateBar(this.appbar, 0).then(() => {
this.showUndo = 0;
this.toast = localize(data); this.toast = localize(data);
utils.timer(5, (val) => { this.animateBar(this.toastbar, 1);
if (!val) this.toast = val; let a = 5;
clearInterval(barTimer);
barTimer = setInterval(() => a-- < 1 && this.hideBar(), 1000);
}); });
}, },
showUndoBar(message) { showUndoBar(message) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
clearTimeout(undoTimer); this.animateBar(this.toastbar, 0);
this.showUndo = true; this.animateBar(this.appbar, 0).then(() => {
this.toast = null;
this.showUndo = 1;
this.snackMsg = message; this.snackMsg = message;
this.countdown = 5; this.countdown = 5;
this.animateBar(this.snackbar, 1).then(() => {
let a = 5; let a = 5;
undoTimer = setInterval(() => { clearInterval(barTimer);
barTimer = setInterval(() => {
if (this.undo) { if (this.undo) {
this.showUndo = this.undo = false; this.hideBar();
clearTimeout(undoTimer); resolve(1);
resolve(true);
} }
this.countdown = Math.round((a -= 0.1)); this.countdown = Math.round((a -= 0.1));
if (this.countdown < 1) { if (this.countdown < 1) {
this.showUndo = false; this.hideBar();
clearTimeout(undoTimer); reject(1);
reject(true);
} }
}, 100); }, 100);
}); });
});
});
}, },
hideBar({ object }) { hideBar() {
this.appbar.translateY = 64; clearInterval(barTimer);
object this.animateBar(this.toast ? this.toastbar : this.snackbar, 0).then(
.animate({ () => {
opacity: 0, this.showUndo = this.undo = 0;
translate: { x: 0, y: 64 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
})
.then(() => {
this.showUndo = false;
this.toast = null; this.toast = null;
this.appbar.animate({ this.animateBar(this.appbar, 1);
translate: { x: 0, y: 0 }, }
duration: 250, );
curve: CoreTypes.AnimationCurve.ease,
});
object.opacity = 1;
object.translateY = 0;
clearTimeout(undoTimer);
});
}, },
undoDel() { undoDel() {
this.undo = true; this.undo = 1;
},
//NAVIGATION HANDLERS
navigateTo() {
this.$navigateTo(CTSettings, {
transition: {
name: this.RTL ? "slideRight" : "slide",
duration: 200,
curve: "easeOut",
},
});
},
navigateBack() {
setNumber("isTimer", 0);
this.hasBackStack
? this.$navigateBack()
: this.$navigateTo(EnRecipes, {
clearHistory: true,
});
}, },
// HELPERS // HELPERS
getRandomID() { keepScreenOnCountUp() {
return Math.floor(Math.random() * 9000000000) + 1000000000; utils.keepScreenOn(
this.activeTimers.filter((e: any) => !e.isPaused).length
);
}, },
}, },
created() { created() {
this.clearTimerInterval(); this.clearTimerInterval();
this.recipeID && this.addTimer();
},
destroyed() {
setNumber("isTimer", 0);
}, },
}; };
</script> </script>

View file

@ -1,15 +1,11 @@
<template> <template>
<Page @loaded="onPageLoad" @unloaded="onPageUnload" actionBarHidden="true"> <Page @loaded="pgLoad" @unloaded="onPageUnload" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *"> <GridLayout rows="*, auto" columns="auto, *, auto">
<ScrollView <ScrollView rowSpan="2" colSpan="3" @scroll="!showUndo && svLoad($event)">
rowSpan="2"
colSpan="2"
@scroll="!showUndo && onScroll($event)"
>
<StackLayout padding="0 16 72"> <StackLayout padding="0 16 72">
<Label class="pageTitle" padding="16 0" :text="`${title}` | L" /> <RLabel class="pageTitle" padding="16 0 24" :text="`${title}` | L" />
<Image <Image
margin="8 0 32" margin="0 0 32"
v-if="recipe.image" v-if="recipe.image"
:src="recipe.image" :src="recipe.image"
stretch="aspectFit" stretch="aspectFit"
@ -20,7 +16,7 @@
/> />
<Button <Button
v-else v-else
margin="8 0 32" margin="0 0 32"
class="ico imgHolder" class="ico imgHolder"
fontSize="128" fontSize="128"
:width="screenWidth - 32" :width="screenWidth - 32"
@ -37,30 +33,33 @@
@loaded="setInputTypeText($event, 'words')" @loaded="setInputTypeText($event, 'words')"
/> />
</StackLayout> </StackLayout>
<GridLayout columns="*, 8, *"> <RGridLayout :rtl="RTL" class="" columns="*, 8, *">
<StackLayout class="inputField"> <StackLayout class="inputField">
<Label class="fieldLabel" :text="'cui' | L" /> <Label class="fieldLabel" :text="'cui' | L" />
<TextField <TextField
@loaded="setGravity"
:text="recipe.cuisine | L" :text="recipe.cuisine | L"
editable="false" editable="false"
@focus="!modalOpen && showCuisine(true)" @focus="!modalOpen && showCuisine(1)"
@tap="showCuisine(false)" @tap="showCuisine(0)"
/> />
</StackLayout> </StackLayout>
<StackLayout class="inputField" col="2"> <StackLayout class="inputField" col="2">
<Label class="fieldLabel" :text="'cat' | L" /> <Label class="fieldLabel" :text="'cat' | L" />
<TextField <TextField
@loaded="setGravity"
ref="category" ref="category"
:text="recipe.category | L" :text="recipe.category | L"
editable="false" editable="false"
@focus="!modalOpen && showCategories(true)" @focus="!modalOpen && showCategories(1)"
@tap="showCategories(false)" @tap="showCategories(0)"
/> />
</StackLayout> </StackLayout>
</GridLayout> </RGridLayout>
<StackLayout class="inputField"> <StackLayout class="inputField">
<Label class="fieldLabel" :text="'ts' | L" /> <Label class="fieldLabel" :text="'ts' | L" />
<TextField <TextField
@loaded="setGravity"
:hint="'tsInfo' | L" :hint="'tsInfo' | L"
autocapitalizationType="words" autocapitalizationType="words"
ref="tags" ref="tags"
@ -69,31 +68,34 @@
returnKeyType="next" returnKeyType="next"
/> />
</StackLayout> </StackLayout>
<GridLayout columns="*, 8, *"> <RGridLayout :rtl="RTL" columns="*, 8, *">
<StackLayout class="inputField"> <StackLayout class="inputField">
<Label class="fieldLabel" :text="'prepT' | L" /> <Label class="fieldLabel" :text="'prepT' | L" />
<TextField <TextField
@loaded="setGravity"
:text="timeRequired('prepTime')" :text="timeRequired('prepTime')"
editable="false" editable="false"
@focus="!modalOpen && setTimeRequired(true, 'prepTime')" @focus="!modalOpen && setTimeRequired(1, 'prepTime')"
@tap="setTimeRequired(false, 'prepTime')" @tap="setTimeRequired(0, 'prepTime')"
/> />
</StackLayout> </StackLayout>
<StackLayout class="inputField" col="2"> <StackLayout class="inputField" col="2">
<Label class="fieldLabel" :text="'cookT' | L" /> <Label class="fieldLabel" :text="'cookT' | L" />
<TextField <TextField
@loaded="setGravity"
ref="cookTime" ref="cookTime"
:text="timeRequired('cookTime')" :text="timeRequired('cookTime')"
editable="false" editable="false"
@focus="!modalOpen && setTimeRequired(true, 'cookTime')" @focus="!modalOpen && setTimeRequired(1, 'cookTime')"
@tap="setTimeRequired(false, 'cookTime')" @tap="setTimeRequired(0, 'cookTime')"
/> />
</StackLayout> </StackLayout>
</GridLayout> </RGridLayout>
<GridLayout columns="*, 8, *"> <RGridLayout :rtl="RTL" columns="*, 8, *">
<StackLayout class="inputField"> <StackLayout class="inputField">
<Label class="fieldLabel" :text="'yieldQ' | L" /> <Label class="fieldLabel" :text="'yieldQ' | L" />
<TextField <TextField
@loaded="setGravity"
ref="yieldQuantity" ref="yieldQuantity"
v-model="recipe.yieldQuantity" v-model="recipe.yieldQuantity"
hint="1" hint="1"
@ -104,22 +106,24 @@
<StackLayout class="inputField" col="2"> <StackLayout class="inputField" col="2">
<Label class="fieldLabel" :text="'yieldU' | L" /> <Label class="fieldLabel" :text="'yieldU' | L" />
<TextField <TextField
@loaded="setGravity"
:text="`${recipe.yieldUnit}` | L" :text="`${recipe.yieldUnit}` | L"
editable="false" editable="false"
@focus="!modalOpen && showYieldUnits(true)" @focus="!modalOpen && showYieldUnits(1)"
@tap="showYieldUnits(false)" @tap="showYieldUnits(0)"
/> />
</StackLayout> </StackLayout>
</GridLayout> </RGridLayout>
<GridLayout columns="*, 8, *"> <GridLayout columns="*, 8, *">
<StackLayout class="inputField"> <StackLayout class="inputField" :col="RTL ? 2 : 0">
<Label class="fieldLabel" :text="'Difficulty level' | L" /> <Label class="fieldLabel" :text="'Difficulty level' | L" />
<TextField <TextField
@loaded="setGravity"
ref="difficultyLevel" ref="difficultyLevel"
:text="`${recipe.difficulty}` | L" :text="`${recipe.difficulty}` | L"
editable="false" editable="false"
@focus="!modalOpen && showDifficultyLevel(true)" @focus="!modalOpen && showDifficultyLevel(1)"
@tap="showDifficultyLevel(false)" @tap="showDifficultyLevel(0)"
/> />
</StackLayout> </StackLayout>
</GridLayout> </GridLayout>
@ -128,7 +132,8 @@
:text="getTitleCount('ings', 'ingredients')" :text="getTitleCount('ings', 'ingredients')"
class="sectionTitle" class="sectionTitle"
/> />
<GridLayout <RGridLayout
:rtl="RTL"
columns="auto,8,auto,8,*,auto" columns="auto,8,auto,8,*,auto"
v-for="(ingredient, index) in recipe.ingredients" v-for="(ingredient, index) in recipe.ingredients"
:key="'ing' + index" :key="'ing' + index"
@ -147,8 +152,8 @@
col="2" col="2"
:text="`${recipe.ingredients[index].unit}` | L" :text="`${recipe.ingredients[index].unit}` | L"
editable="false" editable="false"
@focus="!modalOpen && showUnits($event, true, index)" @focus="!modalOpen && showUnits($event, 1, index)"
@tap="showUnits($event, false, index)" @tap="showUnits($event, 0, index)"
/> />
<TextField <TextField
@ -163,13 +168,14 @@
/> />
<Button <Button
col="5" col="5"
class="ico x" class="ico min"
:text="icon.x" :text="icon.x"
@tap="removeIngredient(index)" @tap="removeIngredient(index)"
/> />
</GridLayout> </RGridLayout>
<Button <Button
class="text big" class="text big hal"
:class="{ r: RTL }"
:text="'aIngBtn' | L" :text="'aIngBtn' | L"
@tap="addIngredient()" @tap="addIngredient()"
/> />
@ -178,7 +184,8 @@
:text="getTitleCount('inss', 'instructions')" :text="getTitleCount('inss', 'instructions')"
class="sectionTitle" class="sectionTitle"
/> />
<GridLayout <RGridLayout
:rtl="RTL"
columns="*,auto" columns="*,auto"
v-for="(instruction, index) in recipe.instructions" v-for="(instruction, index) in recipe.instructions"
:key="'ins' + index" :key="'ins' + index"
@ -190,13 +197,14 @@
/> />
<Button <Button
col="1" col="1"
class="ico x" class="ico min"
:text="icon.x" :text="icon.x"
@tap="removeInstruction(index)" @tap="removeInstruction(index)"
/> />
</GridLayout> </RGridLayout>
<Button <Button
class="text big" class="text big hal"
:class="{ r: RTL }"
:text="'aStpBtn' | L" :text="'aStpBtn' | L"
@tap="addInstruction" @tap="addInstruction"
/> />
@ -205,32 +213,36 @@
:text="getTitleCount('cmbs', 'combinations')" :text="getTitleCount('cmbs', 'combinations')"
class="sectionTitle" class="sectionTitle"
/> />
<GridLayout <RGridLayout
:rtl="RTL"
columns="*,auto" columns="*,auto"
v-for="(combination, index) in recipe.combinations" v-for="(combination, index) in recipe.combinations"
:key="'cmbs' + index" :key="'cmbs' + index"
> >
<TextField <TextField
@loaded="setGravity"
class="combField" class="combField"
:text="getCombinationTitle(combination)" :text="getCombinationTitle(combination)"
editable="false" editable="false"
/> />
<Button <Button
col="1" col="1"
class="ico x" class="ico min"
:text="icon.x" :text="icon.x"
@tap="removeCombination(combination)" @tap="removeCombination(combination)"
/> />
</GridLayout> </RGridLayout>
<Button <Button
class="text big" class="text big hal"
:class="{ r: RTL }"
:text="'addCmbBtn' | L" :text="'addCmbBtn' | L"
@tap="showCombinations" @tap="showCombinations"
/> />
<!-- NOTES --> <!-- NOTES -->
<Label :text="getTitleCount('nos', 'notes')" class="sectionTitle" /> <Label :text="getTitleCount('nos', 'notes')" class="sectionTitle" />
<GridLayout <RGridLayout
columns="*,auto" :rtl="RTL"
columns="*, auto"
v-for="(note, index) in recipe.notes" v-for="(note, index) in recipe.notes"
:key="'nos' + index" :key="'nos' + index"
> >
@ -241,52 +253,53 @@
/> />
<Button <Button
col="1" col="1"
class="ico x" class="ico min"
:text="icon.x" :text="icon.x"
@tap="removeNote(index)" @tap="removeNote(index)"
/> />
</GridLayout> </RGridLayout>
<Button class="text big" :text="'aNoBtn' | L" @tap="addNote" /> <Button
class="text big hal"
:class="{ r: RTL }"
:text="'aNoBtn' | L"
@tap="addNote"
/>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
<GridLayout <RGridLayout
:rtl="RTL && hasChanges"
row="1" row="1"
@loaded="onAppBarLoad" @loaded="abLoad"
:hidden="showUndo" :hidden="showUndo"
class="appbar" class="appbar"
:colSpan="hasChanges ? 2 : 1" :col="RTL ? (hasChanges ? 0 : 2) : 0"
:colSpan="hasChanges ? 3 : 1"
columns="auto, *, auto" columns="auto, *, auto"
> >
<Button class="ico" :text="icon.back" @tap="navigateBack" /> <Button
class="ico"
:class="{ f: RTL }"
:text="icon.back"
@tap="navigateBack(0)"
/>
<Button <Button
v-if="hasChanges && !saving" v-if="hasChanges && !saving"
class="ico fab" class="ico fab"
:text="icon.save" :text="icon.save"
col="2" col="2"
@tap="saveOperation()" @tap="saveOperation"
/> />
<ActivityIndicator col="2" v-if="saving" :busy="saving" /> <ActivityIndicator col="2" v-if="saving" :busy="saving" />
</GridLayout> </RGridLayout>
<SnackBar <SnackBar
:hidden="!showUndo" :hidden="!showUndo"
colSpan="2" colSpan="2"
:count="countdown" :count="countdown"
:msg="snackMsg" :msg="snackMsg"
:undo="undoDel" :undo="undoDel"
:action="hideUndoBar" :action="hideBar"
:onload="sbLoad"
/> />
<!-- <GridLayout
row="1"
class="appbar snackBar"
:hidden="!showUndo"
colSpan="2"
columns="auto, *, auto"
@swipe="hideUndoBar"
>
<Button :text="countdown" class="ico countdown tb" />
<Label class="title" col="1" :text="snackMsg | L" />
<Button class="ico fab" :text="icon.undo" @tap="undoDel" col="3" />
</GridLayout> -->
</GridLayout> </GridLayout>
</Page> </Page>
</template> </template>
@ -294,7 +307,6 @@
<script> <script>
import { import {
AndroidApplication, AndroidApplication,
ApplicationSettings,
File, File,
getFileAccess, getFileAccess,
ImageSource, ImageSource,
@ -304,10 +316,15 @@ import {
Utils, Utils,
Observable, Observable,
CoreTypes, CoreTypes,
Frame,
Application,
GridLayout,
} from "@nativescript/core"; } from "@nativescript/core";
import { getString, setString } from "@nativescript/core/application-settings";
import { localize } from "@nativescript/localize"; import { localize } from "@nativescript/localize";
import { ImageCropper } from "nativescript-imagecropper"; import { ImageCropper } from "nativescript-imagecropper";
import { mapState, mapActions } from "vuex"; import { mapState, mapActions } from "vuex";
import EnRecipes from "./EnRecipes.vue";
import Action from "./modals/Action"; import Action from "./modals/Action";
import ActionWithSearch from "./modals/ActionWithSearch"; import ActionWithSearch from "./modals/ActionWithSearch";
import Confirm from "./modals/Confirm"; import Confirm from "./modals/Confirm";
@ -315,22 +332,17 @@ import Prompt from "./modals/Prompt";
import TimePickerHM from "./modals/TimePickerHM"; import TimePickerHM from "./modals/TimePickerHM";
import * as utils from "~/shared/utils"; import * as utils from "~/shared/utils";
import SnackBar from "./sub/SnackBar"; import SnackBar from "./sub/SnackBar";
let undoTimer; let barTimer;
export default { export default {
components: { components: {
SnackBar, SnackBar,
}, },
props: [ props: ["recipeID", "filterFavourites", "filterTrylater", "dupRecipe"],
"recipeID",
"filterFavourites",
"filterTrylater",
"navigationFromView",
],
data() { data() {
return { return {
title: "newRec", title: "newRec",
recipe: { recipe: {
id: this.recipeID ? this.recipeID : this.getRandomID(), id: this.recipeID || utils.getRandomID(0),
image: null, image: null,
title: null, title: null,
cuisine: "Undefined", cuisine: "Undefined",
@ -354,18 +366,18 @@ export default {
}, },
tempRecipe: {}, tempRecipe: {},
tags: undefined, tags: undefined,
modalOpen: false, modalOpen: 0,
// newRecipeID: null, saving: 0,
saving: false,
cacheImagePath: null, cacheImagePath: null,
unSyncCombinations: [], unSyncCombinations: [],
difficultyLevels: ["Easy", "Moderate", "Challenging"], difficultyLevels: ["Easy", "Moderate", "Challenging"],
appbar: null, appbar: null,
snackbar: null,
scrollPos: 1, scrollPos: 1,
countdown: 5, countdown: 5,
snackMsg: null, snackMsg: null,
showUndo: false, showUndo: 0,
undo: false, undo: 0,
}; };
}, },
computed: { computed: {
@ -376,37 +388,42 @@ export default {
"recipes", "recipes",
"cuisines", "cuisines",
"categories", "categories",
"currentComponent", "selCuisine",
"selectedCuisine", "selCategory",
"selectedCategory", "selTag",
"selectedTag", "theme",
"appTheme", "RTL",
]), ]),
screenWidth() { screenWidth() {
return Screen.mainScreen.widthDIPs; return Screen.mainScreen.widthDIPs;
}, },
hasChanges() { hasChanges() {
return JSON.stringify(this.recipe) !== JSON.stringify(this.tempRecipe); return JSON.stringify(this.recipe) != JSON.stringify(this.tempRecipe);
},
hasBackStack() {
return Frame.topmost().backStack.length;
}, },
}, },
methods: { methods: {
...mapActions([ ...mapActions([
"setComponent",
"addRecipeAction", "addRecipeAction",
"addListItemAction", "addListItemAction",
"unSyncCombinationsAction", "unSyncCombinationsAction",
]), ]),
onPageLoad({ object }) { pgLoad({ object }) {
object.bindingContext = new Observable(); object.bindingContext = new Observable();
this.hijackBackEvent(); this.hijackBackEvent();
}, },
onPageUnload() { onPageUnload() {
this.releaseBackEvent(); this.releaseBackEvent();
}, },
onAppBarLoad({ object }) { abLoad(args) {
this.appbar = object; this.appbar = args.object;
}, },
onScroll(args) { sbLoad({ object }) {
this.snackbar = object;
},
svLoad(args) {
let scrollUp; let scrollUp;
let y = args.scrollY; let y = args.scrollY;
if (y) { if (y) {
@ -416,14 +433,14 @@ export default {
if (!scrollUp && ab == 0) { if (!scrollUp && ab == 0) {
this.appbar.animate({ this.appbar.animate({
translate: { x: 0, y: 64 }, translate: { x: 0, y: 64 },
duration: 250, duration: 200,
curve: CoreTypes.AnimationCurve.ease, curve: CoreTypes.AnimationCurve.ease,
}); });
} else if (scrollUp && ab == 64) { } else if (scrollUp && ab == 64) {
Utils.ad.dismissSoftInput(); Utils.ad.dismissSoftInput();
this.appbar.animate({ this.appbar.animate({
translate: { x: 0, y: 0 }, translate: { x: 0, y: 0 },
duration: 250, duration: 200,
curve: CoreTypes.AnimationCurve.ease, curve: CoreTypes.AnimationCurve.ease,
}); });
} }
@ -432,16 +449,16 @@ export default {
// PHOTO HANDLERS // PHOTO HANDLERS
imageHandler() { imageHandler() {
this.clearEmptyFields(true); this.clearEmptyFields(1);
if (this.recipe.image) { if (this.recipe.image) {
this.modalOpen = true; this.modalOpen = 1;
this.$showModal(Action, { this.$showModal(Action, {
props: { props: {
title: "recPic", title: "recPic",
list: ["aap", "rp"], list: ["aap", "rp"],
}, },
}).then((action) => { }).then((action) => {
this.modalOpen = false; this.modalOpen = 0;
switch (action) { switch (action) {
case "aap": case "aap":
this.imagePicker(); this.imagePicker();
@ -454,12 +471,12 @@ export default {
} else this.imagePicker(); } else this.imagePicker();
}, },
imagePicker() { imagePicker() {
let aT = this.appTheme; let aT = this.theme;
utils.getRecipePhoto().then((uri) => { utils.getRecipePhoto().then((uri) => {
if (uri != null) { if (uri != null) {
this.cacheImagePath = path.join( this.cacheImagePath = path.join(
knownFolders.temp().path, knownFolders.temp().path,
`${this.getRandomID()}.jpg` `${utils.getRandomID(0)}.jpg`
); );
utils.copyPhotoToCache(uri, this.cacheImagePath).then((imgPath) => { utils.copyPhotoToCache(uri, this.cacheImagePath).then((imgPath) => {
if (imgPath) { if (imgPath) {
@ -495,15 +512,27 @@ export default {
} }
}); });
}, },
createDupImage() {
if (this.recipe.image) {
let cachePath = path.join(
knownFolders.temp().path,
`${utils.getRandomID(0)}.jpg`
);
utils.copyPhotoToCache(this.recipe.image, cachePath).then((imgPath) => {
if (imgPath) this.recipe.image = imgPath;
});
}
},
// DATA LIST // DATA LIST
showCuisine(focus) { showCuisine(focus) {
this.modalOpen = true; this.modalOpen = 1;
this.$showModal(Action, { this.$showModal(Action, {
props: { props: {
title: "cui", title: "cui",
list: this.cuisines, list: this.cuisines,
action: "aNBtn", action: "aNBtn",
selected: this.recipe.cuisine,
}, },
}).then((action) => { }).then((action) => {
if (action == "aNBtn") { if (action == "aNBtn") {
@ -513,21 +542,21 @@ export default {
action: "aBtn", action: "aBtn",
}, },
}).then((item) => { }).then((item) => {
this.modalOpen = false; this.modalOpen = 0;
if (item.length) { if (item.length) {
this.recipe.cuisine = item; this.recipe.cuisine = item;
this.addListItemAction({ this.addListItemAction({
item, item,
listName: "cuisines", listName: "cuisines",
}); });
if (focus) this.autoFocusField("category", false); if (focus) this.autoFocusField("category", 0);
} }
}); });
} else { } else {
this.modalOpen = false; this.modalOpen = 0;
if (action) { if (action) {
this.recipe.cuisine = action; this.recipe.cuisine = action;
if (focus) this.autoFocusField("category", false); if (focus) this.autoFocusField("category", 0);
} else } else
this.cuisines.includes(this.recipe.cuisine) this.cuisines.includes(this.recipe.cuisine)
? mull ? mull
@ -536,12 +565,13 @@ export default {
}); });
}, },
showCategories(focus) { showCategories(focus) {
this.modalOpen = true; this.modalOpen = 1;
this.$showModal(Action, { this.$showModal(Action, {
props: { props: {
title: "cat", title: "cat",
list: this.categories, list: this.categories,
action: "aNBtn", action: "aNBtn",
selected: this.recipe.category,
}, },
}).then((action) => { }).then((action) => {
if (action == "aNBtn") { if (action == "aNBtn") {
@ -551,21 +581,21 @@ export default {
action: "aBtn", action: "aBtn",
}, },
}).then((item) => { }).then((item) => {
this.modalOpen = false; this.modalOpen = 0;
if (item.length) { if (item.length) {
this.recipe.category = item; this.recipe.category = item;
this.addListItemAction({ this.addListItemAction({
item, item,
listName: "categories", listName: "categories",
}); });
if (focus) this.autoFocusField("tags", true); if (focus) this.autoFocusField("tags", 1);
} }
}); });
} else { } else {
this.modalOpen = false; this.modalOpen = 0;
if (action) { if (action) {
this.recipe.category = action; this.recipe.category = action;
if (focus) this.autoFocusField("tags", true); if (focus) this.autoFocusField("tags", 1);
} else } else
this.categories.includes(this.recipe.category) this.categories.includes(this.recipe.category)
? mull ? mull
@ -574,12 +604,13 @@ export default {
}); });
}, },
showYieldUnits(focus) { showYieldUnits(focus) {
this.modalOpen = true; this.modalOpen = 1;
this.$showModal(Action, { this.$showModal(Action, {
props: { props: {
title: "yieldU", title: "yieldU",
list: this.yieldUnits, list: this.yieldUnits,
action: "aNBtn", action: "aNBtn",
selected: this.recipe.yieldUnit,
}, },
}).then((action) => { }).then((action) => {
if (action == "aNBtn") { if (action == "aNBtn") {
@ -589,21 +620,21 @@ export default {
action: "aBtn", action: "aBtn",
}, },
}).then((item) => { }).then((item) => {
this.modalOpen = false; this.modalOpen = 0;
if (item.length) { if (item.length) {
this.recipe.yieldUnit = item; this.recipe.yieldUnit = item;
this.addListItemAction({ this.addListItemAction({
item, item,
listName: "yieldUnits", listName: "yieldUnits",
}); });
if (focus) this.autoFocusField("difficultyLevel", false); if (focus) this.autoFocusField("difficultyLevel", 0);
} }
}); });
} else { } else {
this.modalOpen = false; this.modalOpen = 0;
if (action) { if (action) {
this.recipe.yieldUnit = action; this.recipe.yieldUnit = action;
if (focus) this.autoFocusField("difficultyLevel", false); if (focus) this.autoFocusField("difficultyLevel", 0);
} else } else
this.yieldUnits.includes(this.recipe.yieldUnit) this.yieldUnits.includes(this.recipe.yieldUnit)
? mull ? mull
@ -612,14 +643,15 @@ export default {
}); });
}, },
showDifficultyLevel(focus) { showDifficultyLevel(focus) {
this.modalOpen = true; this.modalOpen = 1;
this.$showModal(Action, { this.$showModal(Action, {
props: { props: {
title: "Difficulty level", title: "Difficulty level",
list: this.difficultyLevels, list: this.difficultyLevels,
selected: this.recipe.difficulty,
}, },
}).then((action) => { }).then((action) => {
this.modalOpen = false; this.modalOpen = 0;
if (action) { if (action) {
this.recipe.difficulty = action; this.recipe.difficulty = action;
if (focus) this.addIngredient(); if (focus) this.addIngredient();
@ -630,12 +662,13 @@ export default {
}); });
}, },
showUnits(e, focus, index) { showUnits(e, focus, index) {
this.modalOpen = true; this.modalOpen = 1;
this.$showModal(Action, { this.$showModal(Action, {
props: { props: {
title: "Unit", title: "Unit",
list: this.units, list: this.units,
action: "aNBtn", action: "aNBtn",
selected: this.recipe.ingredients[index].unit,
}, },
}).then((action) => { }).then((action) => {
if (action == "aNBtn") { if (action == "aNBtn") {
@ -645,7 +678,7 @@ export default {
action: "aBtn", action: "aBtn",
}, },
}).then((item) => { }).then((item) => {
this.modalOpen = false; this.modalOpen = 0;
if (item.length) { if (item.length) {
this.recipe.ingredients[index].unit = item; this.recipe.ingredients[index].unit = item;
this.addListItemAction({ this.addListItemAction({
@ -657,7 +690,7 @@ export default {
} }
}); });
} else { } else {
this.modalOpen = false; this.modalOpen = 0;
if (action) { if (action) {
this.recipe.ingredients[index].unit = action; this.recipe.ingredients[index].unit = action;
if (focus && this.recipe.ingredients.length - 1 === index) if (focus && this.recipe.ingredients.length - 1 === index)
@ -668,7 +701,7 @@ export default {
}, },
showCombinations() { showCombinations() {
Utils.ad.dismissSoftInput(); Utils.ad.dismissSoftInput();
this.modalOpen = true; this.modalOpen = 1;
let existingCombinations = [...this.recipe.combinations, this.recipe.id]; let existingCombinations = [...this.recipe.combinations, this.recipe.id];
let filteredRecipes = this.recipes.filter( let filteredRecipes = this.recipes.filter(
(e) => !existingCombinations.includes(e.id) (e) => !existingCombinations.includes(e.id)
@ -679,7 +712,7 @@ export default {
recipes: filteredRecipes, recipes: filteredRecipes,
}, },
}).then((res) => { }).then((res) => {
this.modalOpen = false; this.modalOpen = 0;
if (res) this.recipe.combinations.push(res); if (res) this.recipe.combinations.push(res);
}); });
}, },
@ -697,7 +730,7 @@ export default {
}); });
}, },
removeIngredient(index) { removeIngredient(index) {
this.modalOpen = true; this.modalOpen = 1;
if (this.recipe.ingredients[index].item.length) { if (this.recipe.ingredients[index].item.length) {
let item = this.recipe.ingredients[index]; let item = this.recipe.ingredients[index];
this.recipe.ingredients.splice(index, 1); this.recipe.ingredients.splice(index, 1);
@ -707,7 +740,7 @@ export default {
} else { } else {
this.recipe.ingredients.splice(index, 1); this.recipe.ingredients.splice(index, 1);
} }
setTimeout(() => (this.modalOpen = false), 200); setTimeout(() => (this.modalOpen = 0), 200);
}, },
addInstruction() { addInstruction() {
this.recipe.instructions.push(""); this.recipe.instructions.push("");
@ -759,17 +792,17 @@ export default {
clearEmpty("notes"); clearEmpty("notes");
}, },
saveOperation() { saveOperation() {
this.saving = this.modalOpen = true; this.saving = this.modalOpen = 1;
this.clearEmptyFields(); this.clearEmptyFields();
this.recipe.lastModified = new Date().getTime(); this.recipe.lastModified = new Date().getTime();
ApplicationSettings.setString("previousCuisine", this.recipe.cuisine); setString("previousCuisine", this.recipe.cuisine);
ApplicationSettings.setString("previousCategory", this.recipe.category); setString("previousCategory", this.recipe.category);
ApplicationSettings.setString("previousYieldUnit", this.recipe.yieldUnit); setString("previousYieldUnit", this.recipe.yieldUnit);
if (this.cacheImagePath) { if (this.cacheImagePath) {
let recipeImage = path.join( let recipeImage = path.join(
knownFolders.documents().getFolder("EnRecipes").getFolder("Images") knownFolders.documents().getFolder("EnRecipes").getFolder("Images")
.path, .path,
`${this.getRandomID()}.jpg` `${utils.getRandomID(0)}.jpg`
); );
let binarySource = File.fromPath(this.cacheImagePath).readSync(); let binarySource = File.fromPath(this.cacheImagePath).readSync();
File.fromPath(recipeImage).writeSync(binarySource); File.fromPath(recipeImage).writeSync(binarySource);
@ -794,57 +827,49 @@ export default {
}, },
saveRecipe() { saveRecipe() {
this.addRecipeAction(this.recipe); this.addRecipeAction(this.recipe);
setTimeout(() => (this.saving = false), 100); this.saving = 0;
this.$navigateBack(); this.dupRecipe
? this.$navigateTo(EnRecipes, {
clearHistory: true,
animated: false,
})
: this.goBackAction(0);
}, },
// UNDO OPERATION // UNDO OPERATION
showUndoBar(message) { showUndoBar(message) {
clearInterval(barTimer);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.showUndo = true; this.animateBar(this.appbar, 0).then(() => {
this.appbar.translateY = 0; this.showUndo = 1;
this.snackMsg = message; this.snackMsg = message;
this.countdown = 5; this.countdown = 5;
this.animateBar(this.snackbar, 1).then(() => {
let a = 5; let a = 5;
clearTimeout(undoTimer); barTimer = setInterval(() => {
undoTimer = setInterval(() => {
if (this.undo) { if (this.undo) {
this.showUndo = this.undo = false; this.hideBar();
clearTimeout(undoTimer); resolve(1);
resolve(true);
} }
this.countdown = Math.round((a -= 0.1)); this.countdown = Math.round((a -= 0.1));
if (this.countdown < 1) { if (this.countdown < 1) {
this.showUndo = false; this.hideBar();
clearTimeout(undoTimer); reject(1);
reject(true);
} }
}, 100); }, 100);
}); });
},
hideUndoBar({ object }) {
object
.animate({
opacity: 0,
translate: { x: 0, y: 64 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
})
.then(() => {
this.showUndo = false;
this.appbar.translateY = 64;
this.appbar.animate({
translate: { x: 0, y: 0 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
}); });
object.opacity = 1; });
object.translateY = 0; },
clearTimeout(undoTimer); hideBar() {
clearInterval(barTimer);
this.animateBar(this.snackbar, 0).then(() => {
this.showUndo = this.undo = 0;
this.animateBar(this.appbar, 1);
}); });
}, },
undoDel() { undoDel() {
this.undo = true; this.undo = 1;
}, },
// HELPERS // HELPERS
@ -891,29 +916,30 @@ export default {
}, },
focusField(args, type) { focusField(args, type) {
if (type) this.setInputTypeText(args, type); if (type) this.setInputTypeText(args, type);
else this.setGravity(args);
if (!args.object.text) { if (!args.object.text) {
args.object.focus(); args.object.focus();
setTimeout(() => Utils.ad.showSoftInput(args.object.android), 100); setTimeout(() => Utils.ad.showSoftInput(args.object.android), 100);
} }
}, },
setInputTypeText(args, type) { setInputTypeText({ object }, type) {
let field = args.object; this.setGravity(object);
let common = let common =
android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_CLASS_TEXT |
android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
switch (type) { switch (type) {
case "words": case "words":
field.android.setInputType( object.android.setInputType(
android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS | common android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS | common
); );
break; break;
case "sentence": case "sentence":
field.android.setInputType( object.android.setInputType(
android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | common android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | common
); );
break; break;
case "multiLine": case "multiLine":
field.android.setInputType( object.android.setInputType(
android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE | android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE |
android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES |
common common
@ -921,37 +947,28 @@ export default {
break; break;
} }
}, },
getRandomID() {
let res = "";
let chars = "abcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < 16; i++) {
res += chars.charAt(Math.floor(Math.random() * chars.length));
}
return res;
},
setTimeRequired(focus, time) { setTimeRequired(focus, time) {
this.modalOpen = true; this.modalOpen = 1;
let t = this.recipe[time].split(":"); let t = this.recipe[time].split(":");
let hr = t[0]; let hr = t[0];
let min = t[1]; let min = t[1];
this.$showModal(TimePickerHM, { this.$showModal(TimePickerHM, {
props: { props: {
title: `${time == "prepTime" ? "prepT" : "cookT"}`, title: `${time == "prepTime" ? "prepT" : "cookT"}`,
action: "SET",
selectedHr: hr, selectedHr: hr,
selectedMin: min, selectedMin: min,
}, },
}).then((result) => { }).then((result) => {
this.modalOpen = false; this.modalOpen = 0;
if (result) { if (result) {
this.recipe[time] = result; this.recipe[time] = result;
if (focus) { if (focus) {
switch (time) { switch (time) {
case "prepTime": case "prepTime":
this.autoFocusField("cookTime", false); this.autoFocusField("cookTime", 0);
break; break;
case "cookTime": case "cookTime":
this.autoFocusField("yieldQuantity", true); this.autoFocusField("yieldQuantity", 1);
break; break;
} }
} }
@ -980,12 +997,22 @@ export default {
backEvent(args) { backEvent(args) {
if (!this.modalOpen) { if (!this.modalOpen) {
args.cancel = true; args.cancel = true;
this.navigateBack(); this.navigateBack(1);
} }
}, },
navigateBack() { goBackAction(exit) {
this.hasBackStack || this.recipeID
? this.$navigateBack()
: exit
? Application.android.foregroundActivity.finish()
: this.$navigateTo(EnRecipes, {
clearHistory: true,
animated: false,
});
},
navigateBack(hwb) {
if (this.hasChanges) { if (this.hasChanges) {
this.modalOpen = true; this.modalOpen = 1;
this.$showModal(Confirm, { this.$showModal(Confirm, {
props: { props: {
title: "unsaved", title: "unsaved",
@ -994,46 +1021,47 @@ export default {
okButtonText: "kEdit", okButtonText: "kEdit",
}, },
}).then((action) => { }).then((action) => {
this.modalOpen = false; this.modalOpen = 0;
if (action != null && !action) this.$navigateBack(); if (action != null && !action) {
this.goBackAction(1);
knownFolders.temp().clear();
}
}); });
} else this.$navigateBack(); } else this.goBackAction(hwb);
}, },
}, },
created() { created() {
setTimeout(() => { this.title = this.recipeID || this.dupRecipe ? "editRec" : "newRec";
this.setComponent("EditRecipe");
}, 500);
this.title = this.recipeID ? "editRec" : "newRec";
if (this.recipeID) { if (this.recipeID) {
let recipe = this.recipes.filter((e) => e.id === this.recipeID)[0]; let recipe = this.recipes.filter((e) => e.id === this.recipeID)[0];
Object.assign(this.recipe, JSON.parse(JSON.stringify(recipe))); Object.assign(this.recipe, JSON.parse(JSON.stringify(recipe)));
Object.assign(this.tempRecipe, JSON.parse(JSON.stringify(this.recipe))); Object.assign(this.tempRecipe, JSON.parse(JSON.stringify(this.recipe)));
if (this.recipe.tags.length) this.joinTags(); if (this.recipe.tags.length) this.joinTags();
} else if (this.dupRecipe) {
this.recipe = Object.assign({}, this.dupRecipe);
this.recipe.tried = 1;
this.recipe.lastTried = 0;
this.createDupImage();
} else { } else {
this.recipe.cuisine = this.selectedCuisine this.recipe.cuisine = this.selCuisine
? /all/.test(this.selectedCuisine) ? /all/.test(this.selCuisine)
? "Undefined" ? "Undefined"
: this.selectedCuisine : this.selCuisine
: ApplicationSettings.getString("previousCuisine", "Undefined"); : getString("previousCuisine", "Undefined");
this.recipe.category = this.selectedCategory this.recipe.category = this.selCategory
? /all/.test(this.selectedCategory) ? /all/.test(this.selCategory)
? "Undefined" ? "Undefined"
: this.selectedCategory : this.selCategory
: ApplicationSettings.getString("previousCategory", "Undefined"); : getString("previousCategory", "Undefined");
if (this.selectedTag && !/all/.test(this.selectedTag)) { if (this.selTag && !/all/.test(this.selTag)) {
this.tags = this.selectedTag; this.tags = this.selTag;
this.splitTags(); this.splitTags();
} }
this.recipe.yieldUnit = ApplicationSettings.getString( this.recipe.yieldUnit = getString("previousYieldUnit", "Serving");
"previousYieldUnit", if (this.filterFavourites) this.recipe.favorite = 1;
"Serving" if (this.filterTrylater) this.recipe.tried = 0;
);
if (this.filterFavourites) this.recipe.favorite = true;
if (this.filterTrylater) this.recipe.tried = false;
this.recipe.created = new Date().getTime(); this.recipe.created = new Date().getTime();
Object.assign(this.tempRecipe, JSON.parse(JSON.stringify(this.recipe))); this.tempRecipe = Object.assign({}, this.recipe);
// this.newRecipeID = this.getRandomID();
} }
}, },
}; };

File diff suppressed because it is too large Load diff

View file

@ -1,56 +0,0 @@
<template>
<Page @loaded="onPageLoad">
<ActionBar flat="true">
<GridLayout rows="*" columns="auto, *, auto">
<Button class="ico left" :text="icon.back" @tap="$navigateBack()" />
<Label class="title tb" :text="'grocery' | L" col="1" />
<Button class="ico left" :text="icon.today" col="2" />
</GridLayout>
</ActionBar>
<GridLayout columns="" rows=""> </GridLayout>
</Page>
</template>
<script>
import { ApplicationSettings, Observable } from "@nativescript/core";
import { mapState, mapActions } from "vuex";
export default {
data() {
return {
appTheme: "Light",
};
},
computed: {
...mapState(["icon", "recipes", "mealPlans"]),
isLightMode() {
return this.appTheme === "Light";
},
},
methods: {
...mapActions(["setComponent"]),
onPageLoad({ object }) {
object.bindingContext = new Observable();
this.setComponent("GroceryList");
},
// HELPERS
// NAVIGATION HANDLERS
viewRecipe(recipeID) {
let recipe = this.recipes.filter((e) => e.id === recipeID)[0];
if (recipe) {
this.$navigateTo(ViewRecipe, {
props: {
filterTrylater: true,
recipeID,
},
backstackVisible: false,
});
}
},
// DATA HANDLERS
},
created() {
this.appTheme = ApplicationSettings.getString("appTheme", "Light");
},
};
</script>

View file

@ -1,112 +1,146 @@
<template> <template>
<Page @loaded="pgLoad" actionBarHidden="true"> <Page @loaded="pgLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="*"> <GridLayout rows="*, auto, 72" columns="*">
<ScrollView <ScrollView
@scroll="!edit && svLoad($event)" @scroll="!edit && svScroll($event)"
rowSpan="2" rowSpan="3"
scrollBarIndicatorVisible="false" scrollBarIndicatorVisible="false"
> >
<StackLayout> <StackLayout>
<GridLayout rows="auto" columns="*, auto, 8"> <RGridLayout :rtl="RTL" columns="*, auto, 12">
<Label class="pageTitle" :text="'planner' | L" /> <Label class="pageTitle a" :text="'planner' | L" />
<Button <Button col="1" class="ico" :text="icon.cog" @tap="navigateTo" />
</RGridLayout>
<GridLayout class="monthSwitcher" columns="auto, *, auto">
<Button class="ico min" :text="icon.left" @tap="navigate(0)" />
<Label
class="month"
@touch="mYPicker"
col="1" col="1"
class="ico" :text="formattedDate(0)"
:text="icon.cog" />
@tap="$navigateTo(MPSettings)" <Button
class="ico min"
col="2"
:text="icon.right"
@tap="navigate(1)"
/> />
</GridLayout> </GridLayout>
<GridLayout <RGridLayout
:rtl="RTL"
class="calendar" class="calendar"
columns="*, *, *, *, *, *, *" columns="*, *, *, *, *, *, *"
rows="auto, auto, auto, auto, auto, auto, auto, auto" :rows="calRows"
> >
<Button class="ico navBtn" :text="icon.left" @tap="prevMonth" />
<Label <Label
class="monthName" class="dayName sub rtl"
@touch="touchMonthYearPicker" :class="{ f: RTL }"
col="1"
colSpan="5"
:text="$options.filters.L(mNames[month]) + ' ' + year"
/>
<Button
class="ico navBtn"
col="6"
:text="icon.right"
@tap="nextMonth"
/>
<Label
class="dayName"
row="1"
:col="i" :col="i"
v-for="(d, i) in getDayNames" v-for="(d, i) in getDayNames"
:key="d" :key="d + i"
:text="d | L" :text="d | L"
/> />
<Button <Button
class="min day" v-for="(cal, i) in getCal"
:class="{ :key="i"
tb: isToday(d),
activeDay: isActive(d),
hasPlans: hasPlans(d),
}"
:row="getrow(i)" :row="getrow(i)"
:col="i % 7" :col="i % 7"
v-for="(d, i) in getCal" :class="dayClasses(cal)"
:key="i" :text="cal.ld"
:text="d ? d : null" @tap="setDate(cal)"
@tap="setToday(d)"
/> />
</GridLayout> </RGridLayout>
<StackLayout class="dayPlan"> <StackLayout class="plans">
<StackLayout <RLabel
v-for="(mealType, index) in mealTimesWithRecipes" v-if="plannerView != 'd' && mealPlans.length"
:key="'mealType' + index" class="date tb"
> :text="formattedDate(1)"
<GridLayout columns="auto, auto"> textWrap="true"
/>
<StackLayout v-for="(meal, i) in mealTypes" :key="'meal' + i">
<Label <Label
class="periodLabel tb" :hidden="!getRecipes[meal]"
:class="mealType" class="meal tb"
:text="mealType | L" :class="[meal]"
:text="meal | L"
/> />
<Button <RGridLayout
:visibility="edit ? 'visible' : 'hidden'" :rtl="RTL"
col="1" v-for="(plan, i) in getRecipes[meal]"
class="ico" :key="meal + i"
:text="icon.plus" class="plan"
@tap="addRecipe(mealType)" columns="*, auto"
/>
</GridLayout>
<GridLayout
:columns="`*, ${edit ? 'auto' : 0}`"
v-for="(recipeID, index) in getRecipes[mealType]"
:key="mealType + index"
> >
<Button <RGridLayout
class="recipeTitle" :rtl="RTL"
:text="getRecipeTitle(recipeID)" class="rtl"
@tap="viewRecipe(recipeID)" :hidden="!plan.recipeID"
:columns="noImg ? '*' : '48, *'"
@touch="touchRecipe"
@tap="viewRecipe(plan.id)"
>
<Image
class="imgHolder"
verticalAlignment="middle"
v-if="!noImg && getRecipeImage(plan.recipeID)"
:src="getRecipeImage(plan.recipeID)"
stretch="none"
decodeWidth="48"
decodeHeight="48"
loadMode="async"
/>
<Label
v-else-if="!noImg && !getRecipeImage(plan.recipeID)"
verticalAlignment="middle"
class="ico imgHolder"
@loaded="centerLabel($event, 17)"
width="48"
height="48"
fontSize="24"
:text="icon.img"
/>
<StackLayout class="planContent" col="1">
<RLabel
class="title"
:text="getRecipeTitle(plan.recipeID)"
/>
<RLabel class="attr" :text="getYield(plan.id)" />
</StackLayout>
</RGridLayout>
<Label
class="planContent tw"
@loaded="centerLabel($event, 16)"
:hidden="!plan.note"
:text="plan.note"
/> />
<Button <Button
:visibility="edit ? 'visible' : 'hidden'" :hidden="!edit"
col="1" col="1"
class="ico x" class="ico min"
:text="icon.x" :text="icon.x"
@tap="removeRecipe(mealType, recipeID)" @tap="removeRecipe(plan.id)"
/> />
</GridLayout> </RGridLayout>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
<GridLayout <GridLayout rowSpan="2" rows="*, auto" v-if="!mealPlans.length">
row="1" <StackLayout row="1" class="emptyState">
<RLabel class="title" :text="'ehwmp' | L" />
<RLabel :text="'plsCrt' | L" />
</StackLayout>
</GridLayout>
<RGridLayout
:rtl="RTL"
row="2"
@loaded="abLoad" @loaded="abLoad"
class="appbar" class="appbar"
:hidden="showUndo" :hidden="showUndo"
columns="auto, *, auto, auto" columns="auto, *, auto, auto, auto"
@swipe="stSwipe"
> >
<Button class="ico" :text="icon.back" @tap="$navigateBack()" /> <Button class="ico rtl" :text="icon.back" @tap="navigateBack" />
<Button <Button
class="ico" class="ico"
:text="icon.tod" :text="icon.tod"
@ -115,13 +149,22 @@
col="2" col="2"
/> />
<Button <Button
class="ico fab" :hidden="!hasRecipes"
class="ico"
:text="edit ? icon.done : icon.edit" :text="edit ? icon.done : icon.edit"
@tap="toggleEditMode" @tap="toggleEditMode"
col="3" col="3"
/> />
</GridLayout> <!-- <Button
class="ico"
:text="hasRecipes ? (edit ? icon.done : icon.edit) : icon.madd"
@tap="hasRecipes ? toggleEditMode() : randomMealPlan()"
col="3"
/> -->
<Button class="ico fab" :text="icon.plus" @tap="addMealPlan" col="4" />
</RGridLayout>
<SnackBar <SnackBar
row="2"
:hidden="!showUndo" :hidden="!showUndo"
:count="countdown" :count="countdown"
:msg="snackMsg" :msg="snackMsg"
@ -133,14 +176,21 @@
</Page> </Page>
</template> </template>
<script> <script lang="ts">
import { Observable, CoreTypes } from "@nativescript/core"; import { Frame, Observable, CoreTypes, Screen } from "@nativescript/core";
import { mapState, mapActions } from "vuex"; import { mapState, mapActions } from "vuex";
import ViewRecipe from "./ViewRecipe"; import ViewRecipe from "./ViewRecipe.vue";
import MPSettings from "./settings/MPSettings"; import EditRecipe from "./EditRecipe.vue";
import ActionWithSearch from "./modals/ActionWithSearch"; import EnRecipes from "./EnRecipes.vue";
import MonthYearPicker from "./modals/MonthYearPicker"; import MPSettings from "./settings/MPSettings.vue";
import SnackBar from "./sub/SnackBar"; import Action from "./modals/Action.vue";
import ActionWithSearch from "./modals/ActionWithSearch.vue";
import Prompt from "./modals/Prompt.vue";
import DMYPicker from "./modals/DMYPicker.vue";
import SnackBar from "./sub/SnackBar.vue";
import * as utils from "~/shared/utils";
const Intl = require("nativescript-intl");
import { localize } from "@nativescript/localize";
let barTimer; let barTimer;
export default { export default {
@ -149,10 +199,9 @@ export default {
}, },
data() { data() {
return { return {
mealTimes: ["breakfast", "lunch", "dinner", "snacks"], mealTypes: ["breakfast", "lunch", "dinner", "snacks"],
dNames: ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"],
year: 2021, year: 2021,
mNames: [ monthNames: [
"January", "January",
"February", "February",
"March", "March",
@ -168,20 +217,27 @@ export default {
], ],
month: 0, month: 0,
date: null, date: null,
edit: false, edit: 0,
scrollPos: 1, scrollPos: 1,
appbar: null, appbar: null,
snackbar: null, snackbar: null,
countdown: 5, countdown: 5,
snackMsg: null, snackMsg: null,
showUndo: false, showUndo: 0,
undo: false, undo: 0,
MPSettings: MPSettings,
temp: 0, temp: 0,
}; };
}, },
computed: { computed: {
...mapState(["icon", "recipes", "mealPlans", "mondayFirst"]), ...mapState([
"icon",
"recipes",
"layout",
"mealPlans",
"mondayFirst",
"RTL",
"plannerView",
]),
todaysTime() { todaysTime() {
return new Date(this.year, this.month, this.date, 0).getTime(); return new Date(this.year, this.month, this.date, 0).getTime();
}, },
@ -189,33 +245,58 @@ export default {
if (this.mealPlans.length) { if (this.mealPlans.length) {
return this.mealPlans.reduce((acc, e) => { return this.mealPlans.reduce((acc, e) => {
if (e.date == this.todaysTime) { if (e.date == this.todaysTime) {
acc[e.type] = [...(acc[e.type] || []), e.title]; acc[e.mealType] = [...(acc[e.mealType] || []), e];
} }
return acc; return acc;
}, {}); }, {});
} else return 0; } else return {};
},
calRows() {
let h = (Screen.mainScreen.widthDIPs - 32) / 8;
let pv = this.plannerView;
return pv != "d" ? `${h}, `.repeat(pv == "wk" ? 1 : 6) + h : 0;
}, },
getDayNames() { getDayNames() {
let dNames = [...this.dNames]; let dNames =
if (!this.mondayFirst) dNames.unshift(dNames.pop()); this.plannerView != "d" &&
this.getCal.slice(0, 7).map((d) => {
let date = new Date(d.y, d.m, d.d);
return new Intl.DateTimeFormat(null, {
weekday: "short",
}).format(date);
});
return dNames; return dNames;
}, },
getCal() { getCal() {
let y = this.year; const getDays = (s, e) => {
let m = this.month; let a = [];
let ds = new Date(y, m + 1, 0).getDate(); for (
let fd = new Date(y, m, 1).getDay(); let d = new Date(s);
let ld = new Date(y, m, ds).getDay(); d <= new Date(e);
if (this.mondayFirst) fd -= 1; d.setDate(d.getDate() + 1)
let days = new Array(fd).fill(0); ) {
// let prevDays = Array.from( a.push({
// { length: fd }, d: d.getDate(),
// (e, k) => k + new Date(!m ? y - 1 : y, m, 0).getDate() - fd + 1 ld: this.getLocaleN(d.getDate()),
// ); m: d.getMonth(),
// let days = prevDays; y: d.getFullYear(),
for (let i = 1; i <= ds; i++) days.push(i); });
// for (let i = 1; i <= 6 - ld; i++) days.push(i); }
return days; return a;
};
let pv = this.plannerView;
let date = new Date(
this.year,
this.month,
pv == "mnth" ? 1 : this.date - this.mondayFirst
);
return pv != "d"
? getDays(
date.setDate(date.getDate() - date.getDay() + this.mondayFirst),
date.setDate(date.getDate() + (pv == "mnth" ? 41 : 6))
)
: 0;
}, },
isExactlyToday() { isExactlyToday() {
let d = new Date(); let d = new Date();
@ -225,22 +306,24 @@ export default {
this.date == d.getDate() this.date == d.getDate()
); );
}, },
mealTimesWithRecipes() { hasRecipes() {
return this.mealTimes.filter( return this.mealTypes.filter(
(e) => (this.getRecipes[e] && this.getRecipes[e].length) || this.edit (e) => this.getRecipes[e] && this.getRecipes[e].length
); ).length;
},
noImg() {
return /simple|minimal/.test(this.layout);
},
noAttr() {
return /minimal/.test(this.layout);
}, },
}, },
methods: { methods: {
...mapActions([ ...mapActions(["addMealPlanAction", "deleteMealPlanAction"]),
"setComponent",
"addMealPlanAction",
"deleteMealPlanAction",
]),
pgLoad({ object }) { pgLoad({ object }) {
object.bindingContext = new Observable(); object.bindingContext = new Observable();
this.setComponent("MealPlanner");
if (!this.date || this.date === new Date().getDate()) this.goToToday(); if (!this.date || this.date === new Date().getDate()) this.goToToday();
this.showBar();
}, },
abLoad({ object }) { abLoad({ object }) {
this.appbar = object; this.appbar = object;
@ -248,7 +331,7 @@ export default {
sbLoad({ object }) { sbLoad({ object }) {
this.snackbar = object; this.snackbar = object;
}, },
svLoad(args) { svScroll(args) {
let scrollUp; let scrollUp;
let y = args.scrollY; let y = args.scrollY;
if (y) { if (y) {
@ -258,96 +341,143 @@ export default {
if (!scrollUp && ab == 0) { if (!scrollUp && ab == 0) {
this.appbar.animate({ this.appbar.animate({
translate: { x: 0, y: 64 }, translate: { x: 0, y: 64 },
duration: 250, duration: 200,
curve: CoreTypes.AnimationCurve.ease, curve: CoreTypes.AnimationCurve.ease,
}); });
} else if (scrollUp && ab == 64) { } else if (scrollUp && ab == 64) {
this.appbar.animate({ this.appbar.animate({
translate: { x: 0, y: 0 }, translate: { x: 0, y: 0 },
duration: 250, duration: 200,
curve: CoreTypes.AnimationCurve.ease, curve: CoreTypes.AnimationCurve.ease,
}); });
} }
} }
}, },
// HELPERS
showAppBar() { // Helpers
this.appbar.translateY = 0; centerLabel({ object }, n) {
object.android.setGravity(n);
},
showBar() {
// this.appbar.translateY = 0;
this.appbar.animate({
translate: { x: 0, y: 0 },
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
});
}, },
getrow(i) { getrow(i) {
return Math.floor(2 + i / 7); return Math.floor(1 + i / 7);
}, },
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();
}, },
getRecipeImage(id) {
let r = this.recipes.filter((e) => e.id === id)[0];
return r && r.image;
},
getRecipeTitle(id) { getRecipeTitle(id) {
let recipe = this.recipes.filter((e) => e.id === id)[0]; let r = this.recipes.filter((e) => e.id === id)[0];
return recipe ? recipe.title : `[ ${this.$options.filters.L("resNF")} ]`; return r ? r.title : `[${this.$options.filters.L("resNF")}]`;
},
getRecipeTotalTime(id) {
let r = this.recipes.filter((e) => e.id === id)[0];
return r ? this.totalTime(r.prepTime, r.cookTime).time : "00:00";
},
getYield(id) {
let mp = this.mealPlans.filter((e) => e.id == id)[0];
let r = this.recipes.filter((e) => e.id === mp.recipeID)[0];
return r ? `${this.getLocaleN(mp.quantity)} ${localize(r.yieldUnit)}` : 1;
}, },
// NAVIGATION HANDLERS // NavigationHandlers
viewRecipe(recipeID) { viewRecipe(id) {
let recipe = this.recipes.filter((e) => e.id === recipeID)[0]; let mp = this.mealPlans.filter((e) => e.id == id)[0];
if (recipe) { let r = this.recipes.filter((e) => e.id === mp.recipeID)[0];
if (r) {
this.$navigateTo(ViewRecipe, { this.$navigateTo(ViewRecipe, {
props: { props: {
filterTrylater: true, filterTrylater: 1,
recipeID, recipeID: r.id,
yieldQuantity: mp.quantity,
}, },
}); });
} }
}, },
navigateTo() {
// CALENDAR this.$navigateTo(MPSettings, {
prevMonth() { transition: {
if (this.month == 0) { name: this.RTL ? "slideRight" : "slide",
this.year--; duration: 200,
this.month = 11; curve: "easeOut",
} else this.month--;
this.showAppBar();
}, },
nextMonth() { });
if (this.month == 11) { },
this.year++; navigateBack() {
this.month = 0; Frame.topmost().backStack.length
} else this.month++; ? this.$navigateBack()
this.showAppBar(); : this.$navigateTo(EnRecipes, {
clearHistory: true,
});
},
// Calendar
navigate(dir) {
if (this.RTL) dir = !dir;
let pv = this.plannerView;
let date = new Date(this.year, this.month, this.date);
let sd =
pv == "mnth"
? new Date(this.year, this.month + (dir ? 1 : 0), 0).getDate()
: pv == "wk"
? 7
: 1;
date.setDate(date.getDate() + (dir ? sd : -sd));
this.date = date.getDate();
this.month = date.getMonth();
this.year = date.getFullYear();
this.showBar();
}, },
goToToday() { goToToday() {
let d = new Date(); let d = new Date();
this.year = d.getFullYear(); this.year = d.getFullYear();
this.month = d.getMonth(); this.month = d.getMonth();
this.date = d.getDate(); this.date = d.getDate();
this.showAppBar();
}, },
isToday(date) { dayClasses({ d, m }) {
let d = new Date(); let classes = "min ";
return ( let dt1 = new Date();
this.year == d.getFullYear() && let dt2 = new Date(this.year, m, d, 0).getTime();
this.month == d.getMonth() && if (
date == d.getDate() this.year == dt1.getFullYear() &&
); this.month == dt1.getMonth() &&
m == dt1.getMonth() &&
d == dt1.getDate()
)
classes += "tb ";
classes += this.date == d && this.month == m ? "hl " : "fb ";
if (!!this.mealPlans.filter((e) => e.date == dt2).length)
classes += "accent ";
if (this.month != m) classes += "sub";
return classes;
}, },
isActive(date) { setDate({ d, m, y }) {
return this.date == date; this.year = y;
}, this.month = m;
hasPlans(date) { this.date = d;
let d = new Date(this.year, this.month, date, 0).getTime(); this.showBar();
return this.mealPlans.filter((e) => e.date == d).length;
},
setToday(date) {
if (date) this.date = date;
}, },
toggleEditMode() { toggleEditMode() {
this.edit = !this.edit; this.edit = !this.edit;
}, },
openMonthYearPicker() { openMonthYearPicker() {
this.$showModal(MonthYearPicker, { this.$showModal(DMYPicker, {
props: { props: {
title: "gtD", title: "gtD",
monthNames: this.mNames, monthNames: this.monthNames,
currentD: this.date,
currentM: this.month, currentM: this.month,
currentY: this.year, currentY: this.year,
}, },
@ -355,69 +485,140 @@ export default {
if (res) { if (res) {
this.month = res.month; this.month = res.month;
this.year = res.year; this.year = res.year;
this.date = res.date;
} }
}); });
}, },
stSwipe({ direction }) {
let date = new Date(this.year, this.month, this.date);
if (direction == 1) date.setDate(date.getDate() - 1);
else if (direction == 2) date.setDate(date.getDate() + 1);
this.date = date.getDate();
this.month = date.getMonth();
this.year = date.getFullYear();
},
randomMealPlan() {},
// DATA HANDLERS // DataHandlers
newMealPlan({ date, type, title, index, inDB }) { newMealPlan({ plan, index, inDB }) {
this.addMealPlanAction({ this.addMealPlanAction({
date: date ? date : this.todaysTime, plan,
type,
title,
index, index,
inDB, inDB,
}); });
}, },
addRecipe(type) { addMealPlan() {
let filteredRecipes = this.recipes.filter((e) => this.$showModal(Action, {
this.getRecipes[type] ? !this.getRecipes[type].includes(e.id) : true props: {
title: "add",
list: ["rec", "no"],
},
}).then((type) => {
if (type) {
this.$showModal(Action, {
props: {
title: "selMT",
list: ["breakfast", "lunch", "dinner", "snacks"],
},
}).then((mealType) => {
if (mealType) {
if (type == "rec") {
let recipes = this.recipes.filter((e) =>
this.getRecipes[mealType]
? this.getRecipes[mealType].every((f) => f.recipeID != e.id)
: 1
); );
this.$showModal(ActionWithSearch, { this.$showModal(ActionWithSearch, {
props: { props: {
title: "selRec", title: "selRec",
recipes: filteredRecipes, recipes,
action: "aNBtn",
}, },
}).then( }).then((res) => {
(title) => if (res == "aNBtn") {
title && this.$navigateTo(EditRecipe, {
this.newMealPlan({ date: 0, type, title, index: null, inDB: true }) animated: false,
); });
} else if (res) {
let r = this.recipes.filter((e) => e.id == res)[0];
this.$showModal(Prompt, {
props: {
title: `${localize("req", localize(r.yieldUnit))}`,
placeholder: Math.abs(parseFloat(r.yieldQuantity)),
action: "SET",
},
}).then((quantity) => {
if (quantity) {
let plan = {
id: utils.getRandomID(),
date: this.todaysTime,
mealType,
recipeID: res,
quantity,
note: null,
};
this.newMealPlan({
plan,
index: null,
inDB: 1,
});
}
});
}
});
} else if (type == "no") {
this.$showModal(Prompt, {
props: {
title: "no",
type: "view",
action: "ADD",
},
}).then((note) => {
if (note) {
let plan = {
id: utils.getRandomID(),
date: this.todaysTime,
mealType,
recipeID: null,
quantity: null,
note,
};
this.newMealPlan({
plan,
index: null,
inDB: 1,
});
}
});
}
}
});
}
});
}, },
deleteTempFromDB() { deleteTempFromDB() {
if (this.temp) { if (this.temp) {
this.temp.inDB = 1; let { plan, index } = this.temp;
this.deleteMealPlanAction(this.temp); this.deleteMealPlanAction({ id: plan.id, index, inDB: 1 });
this.temp = 0; this.temp = 0;
} }
}, },
removeRecipe(type, title) { removeRecipe(id) {
this.deleteTempFromDB(); this.deleteTempFromDB();
let date = this.todaysTime; let index = this.mealPlans.findIndex((e) => e.id == id);
let index = this.mealPlans.findIndex( let plan = this.mealPlans.filter((e) => e.id == id)[0];
(e) => e.date == date && e.type == type && e.title == title this.temp = { plan, index };
); this.deleteMealPlanAction({ id, index });
let mealPlan = { this.showUndoBar(plan.note ? "rmN" : "recRm")
date, .then(() => this.newMealPlan({ plan, index }))
type,
title,
index,
};
let temp;
this.temp = temp = mealPlan;
this.deleteMealPlanAction(mealPlan);
this.showUndoBar("recRm")
.then(() => this.newMealPlan({ date, type, title, index }))
.catch(() => { .catch(() => {
temp.inDB = 1; this.deleteMealPlanAction({ id, index, inDB: 1 });
console.log("deleting inDB after catch: ", temp);
this.deleteMealPlanAction(temp);
}); });
}, },
showUndoBar(message) { showUndoBar(message) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.animateBar(this.appbar, 0).then(() => { this.animateBar(this.appbar, 0).then(() => {
this.showUndo = true; this.showUndo = 1;
this.snackMsg = message; this.snackMsg = message;
this.countdown = 5; this.countdown = 5;
this.animateBar(this.snackbar, 1).then(() => { this.animateBar(this.snackbar, 1).then(() => {
@ -426,12 +627,12 @@ export default {
barTimer = setInterval(() => { barTimer = setInterval(() => {
if (this.undo) { if (this.undo) {
this.hideBar(); this.hideBar();
resolve(true); resolve(1);
} }
this.countdown = Math.round((a -= 0.1)); this.countdown = Math.round((a -= 0.1));
if (this.countdown < 1) { if (this.countdown < 1) {
this.hideBar(); this.hideBar();
reject(true); reject(1);
} }
}, 100); }, 100);
}); });
@ -440,22 +641,76 @@ export default {
}, },
hideBar() { hideBar() {
clearInterval(barTimer); clearInterval(barTimer);
this.deleteTempFromDB();
this.animateBar(this.snackbar, 0).then(() => { this.animateBar(this.snackbar, 0).then(() => {
this.showUndo = this.undo = false; this.showUndo = this.undo = 0;
this.animateBar(this.appbar, 1); this.animateBar(this.appbar, 1);
}); });
}, },
undoDel() { undoDel() {
this.undo = true; this.undo = 1;
}, },
//HELPERS // Helpers
touchMonthYearPicker({ object, action }) { formattedDate(v) {
object.className = action.match(/down|move/) let d = new Date(this.year, this.month, this.date, 0, 0, 0);
? "monthName fade" let today = new Date();
: "monthName"; let myToday = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate(),
0,
0,
0
);
let tdy = myToday.getTime();
let ystr = myToday.setDate(today.getDate() - 1);
let tmrw = myToday.setDate(today.getDate() + 1);
let options: {
year?: string;
month?: string;
weekday?: string;
day?: string;
} = {};
if (v) {
options.weekday = "long";
options.day = "numeric";
options.month = "long";
} else {
options.year = "numeric";
options.month = "long";
}
if (this.plannerView == "d") {
options.weekday = "long";
options.day = "numeric";
options.month = "short";
}
let date = new Intl.DateTimeFormat(null, options).format(d);
let val;
switch (d.getTime()) {
case ystr:
val = "ystr";
break;
case tdy:
val = "tdy";
break;
case tmrw:
val = "tmrw";
break;
}
return v
? [ystr, tdy, tmrw].some((e) => e == d.getTime())
? localize(val)
: date
: date;
},
mYPicker({ object, action }) {
object.className = action.match(/down|move/) ? "month fade" : "month";
if (action == "up") this.openMonthYearPicker(); if (action == "up") this.openMonthYearPicker();
}, },
touchRecipe({ object, action }) {
object.className = action.match(/down|move/) ? "fade" : "";
},
}, },
}; };
</script> </script>

View file

@ -1,16 +1,21 @@
<template> <template>
<Page @loaded="onPageLoad" @unloaded="onPageUnload" actionBarHidden="true"> <Page @loaded="pgLoad" @unloaded="onPageUnload" actionBarHidden="true">
<GridLayout rows="*, auto" columns="*"> <GridLayout rows="*, auto, auto" columns="auto, *, auto">
<DockLayout stretchLastChild="true" rowSpan="2"> <DockLayout stretchLastChild="true" rowSpan="3" colSpan="3">
<GridLayout <RGridLayout
:rtl="RTL"
dock="top" dock="top"
rows="auto,auto" rows="auto,auto"
columns="*, auto" columns="*, auto"
paddingBottom="24" paddingBottom="24"
> >
<StackLayout> <StackLayout>
<Label class="pageTitle" paddingBottom="8" :text="recipe.title" /> <RLabel class="pageTitle" paddingBottom="8" :text="recipe.title" />
<StackLayout marginLeft="12" orientation="horizontal"> <StackLayout
:class="{ f: RTL }"
margin="0 12"
orientation="horizontal"
>
<Button <Button
class="ico rate" class="ico rate"
:class="{ rated: recipe.rating >= n }" :class="{ rated: recipe.rating >= n }"
@ -34,72 +39,82 @@
decodeHeight="96" decodeHeight="96"
@tap="viewPhoto" @tap="viewPhoto"
/> />
</GridLayout> </RGridLayout>
<AbsoluteLayout dock="bottom"> <AbsoluteLayout dock="bottom">
<ScrollView <ScrollView
dock="bottom" dock="bottom"
width="100%" width="100%"
height="100%" height="100%"
@loaded="onScrollLoad" @loaded="onScrollLoad"
@scroll="onScroll($event)" @scroll="svScroll($event)"
> >
<StackLayout> <StackLayout>
<GridLayout rows="auto" columns="*, *"> <RGridLayout :rtl="RTL" rows="auto" columns="*, *">
<StackLayout class="attribute"> <StackLayout class="attribute">
<Label class="title sub" :text="'cui' | L" /> <RLabel class="sub" :text="'cui' | L" />
<Label class="value" :text="recipe.cuisine | L" /> <RLabel class="value" :text="recipe.cuisine | L" />
</StackLayout> </StackLayout>
<StackLayout class="attribute" col="1"> <StackLayout class="attribute" col="1">
<Label class="title sub" :text="'cat' | L" /> <RLabel class="sub" :text="'cat' | L" />
<Label class="value" :text="recipe.category | L" /> <RLabel class="value" :text="recipe.category | L" />
</StackLayout> </StackLayout>
</GridLayout> </RGridLayout>
<StackLayout :hidden="!recipe.tags.length" class="attribute"> <StackLayout
<Label class="title sub" :text="'ts' | L" /> :hidden="!recipe.tags.length"
<Label class="value" :text="getTags(recipe.tags)" /> class="attribute hal"
:class="{ r: RTL }"
>
<RLabel class="sub" :text="'ts' | L" />
<RLabel class="value" :text="getTags(recipe.tags)" />
</StackLayout> </StackLayout>
<GridLayout rows="auto" columns="*, *"> <RGridLayout :rtl="RTL" rows="auto" columns="*, *">
<StackLayout <StackLayout
class="attribute" class="attribute"
:hidden="!hasTime(recipe.prepTime)" :hidden="!hasTime(recipe.prepTime)"
> >
<Label class="title sub" :text="'prepT' | L" /> <RLabel class="sub" :text="'prepT' | L" />
<Label class="value" :text="formattedTime(recipe.prepTime)" /> <RLabel
class="value"
:text="formattedTime(recipe.prepTime)"
/>
</StackLayout> </StackLayout>
<StackLayout <StackLayout
:col="hasTime(recipe.prepTime) ? 1 : 0" :col="hasTime(recipe.prepTime) ? 1 : 0"
class="attribute" class="attribute"
:hidden="!hasTime(recipe.cookTime)" :hidden="!hasTime(recipe.cookTime)"
> >
<Label class="title sub" :text="'cookT' | L" /> <RLabel class="title sub" :text="'cookT' | L" />
<Label class="value" :text="formattedTime(recipe.cookTime)" /> <RLabel
class="value"
:text="formattedTime(recipe.cookTime)"
/>
</StackLayout> </StackLayout>
</GridLayout> </RGridLayout>
<GridLayout rows="auto" columns="*, *"> <RGridLayout :rtl="RTL" rows="auto" columns="*, *">
<StackLayout class="attribute"> <StackLayout class="attribute">
<Label class="title sub" :text="'yld' | L" /> <RLabel class="title sub" :text="'yld' | L" />
<Label <RLabel
@touch="touchYield" @touch="touchYield"
class="value clickable" class="value accent"
horizontalAlignment="left"
:text="`${tempYieldQuantity} ${$options.filters.L( :text="`${tempYieldQuantity} ${$options.filters.L(
recipe.yieldUnit recipe.yieldUnit
)}`" )}`"
/> />
</StackLayout> </StackLayout>
<StackLayout class="attribute" col="1"> <StackLayout class="attribute" col="1">
<Label class="title sub" :text="'Difficulty level' | L" /> <RLabel class="title sub" :text="'Difficulty level' | L" />
<Label class="value" :text="recipe.difficulty | L" /> <RLabel class="value" :text="recipe.difficulty | L" />
</StackLayout> </StackLayout>
</GridLayout> </RGridLayout>
<StackLayout @loaded="onIngsLoad"> <StackLayout @loaded="onIngsLoad">
<Label <RLabel
padding="0 16" padding="0 16"
class="sectionTitle" class="sectionTitle"
:hidden="!recipe.ingredients.length" :hidden="!recipe.ingredients.length"
:text="getTitleCount('ings', 'ingredients')" :text="getTitleCount('ings', 'ingredients')"
/> />
<StackLayout <RStackLayout
:rtl="RTL"
orientation="horizontal" orientation="horizontal"
v-for="(item, index) in recipe.ingredients" v-for="(item, index) in recipe.ingredients"
:key="index + 'ing'" :key="index + 'ing'"
@ -107,39 +122,29 @@
@touch="touchIngredient($event, index)" @touch="touchIngredient($event, index)"
> >
<Button class="ico min" :text="icon.uncheck" /> <Button class="ico min" :text="icon.uncheck" />
<Label <RLabel class="value tw" :text="getIngredientItem(item)" />
class="value tw" </RStackLayout>
:text="`${
roundedQuantity(item.quantity)
? roundedQuantity(item.quantity) + ' '
: ''
}${
roundedQuantity(item.quantity)
? $options.filters.L(item.unit) + ' '
: ''
}${item.item}`"
/>
</StackLayout>
</StackLayout> </StackLayout>
<StackLayout @loaded="onInsLoad"> <StackLayout @loaded="onInsLoad">
<Label <RLabel
padding="0 16" padding="0 16"
:hidden="!recipe.instructions.length" :hidden="!recipe.instructions.length"
class="sectionTitle" class="sectionTitle"
:text="getTitleCount('inss', 'instructions')" :text="getTitleCount('inss', 'instructions')"
/> />
<StackLayout <RStackLayout
:rtl="RTL"
orientation="horizontal" orientation="horizontal"
@touch="touchInstruction" @touch="touchInstruction"
v-for="(instruction, index) in recipe.instructions" v-for="(instruction, index) in recipe.instructions"
:key="index + 'ins'" :key="index + 'ins'"
class="instruction" class="instruction"
> >
<Button class="count ico min" :text="index + 1" /> <Button class="count ico min" :text="getLocaleN(index + 1)" />
<Label class="value tw" :text="instruction" /> <RLabel class="value tw" :text="instruction" />
</RStackLayout>
</StackLayout> </StackLayout>
</StackLayout> <RLabel
<Label
@loaded="onCmbLoad" @loaded="onCmbLoad"
padding="0 16" padding="0 16"
:hidden="!recipe.combinations.length" :hidden="!recipe.combinations.length"
@ -153,7 +158,7 @@
:text="getCombinationTitle(combination)" :text="getCombinationTitle(combination)"
@tap="viewCombination(combination)" @tap="viewCombination(combination)"
/> />
<Label <RLabel
@loaded="onNosTLoad" @loaded="onNosTLoad"
padding="0 16" padding="0 16"
:hidden="!recipe.notes.length" :hidden="!recipe.notes.length"
@ -161,17 +166,10 @@
:text="getTitleCount('nos', 'notes')" :text="getTitleCount('nos', 'notes')"
/> />
<StackLayout @loaded="onNosLoad" padding="0 16"> </StackLayout> <StackLayout @loaded="onNosLoad" padding="0 16"> </StackLayout>
<Label <Label class="dateInfo sub tw" :text="getDates().uc" />
class="dateInfo sub tw"
:text="`${$options.filters.L('Last updated')}: ${formattedDate(
recipe.lastModified
)}\n${$options.filters.L('Created')}: ${formattedDate(
recipe.created
)}`"
/>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
<Label <RLabel
@loaded="onStickyLoad" @loaded="onStickyLoad"
class="sectionTitle sticky" class="sectionTitle sticky"
:hidden="!stickyTitle" :hidden="!stickyTitle"
@ -181,61 +179,85 @@
</DockLayout> </DockLayout>
<GridLayout <GridLayout
row="1" row="1"
@loaded="onAppBarLoad" @loaded="sbload"
class="appbar" class="appbar sidebar"
v-show="!toast" :col="RTL ? 0 : 2"
columns="auto, *, auto, auto, auto, auto, auto" rows="auto, auto, auto"
> >
<Button class="ico" :text="icon.timer" @tap="openCookingTimer" />
<Button <Button
row="1"
:hidden="busyDup"
class="ico" class="ico"
:text="photoOpen ? icon.x : icon.back" :class="{ f: RTL }"
@tap="navigateBack" :text="icon.dup"
@tap="duplicateRecipe"
/> />
<ActivityIndicator row="1" :hidden="!busyDup" :busy="busyDup" />
<Button
:hidden="!hasPrinterSupport"
row="2"
class="ico"
:text="icon.print"
@tap="printView"
/>
</GridLayout>
<RGridLayout
:rtl="RTL"
row="2"
colSpan="3"
@loaded="abLoad"
class="appbar"
:hidden="toast"
columns="auto, *, auto, auto, auto, auto"
@touch="() => null"
>
<Button class="ico rtl" :text="icon.back" @tap="$navigateBack()" />
<Button <Button
col="2" col="2"
class="ico"
:text="icon.timer"
@tap="openCookingTimer"
/>
<Button
col="3"
v-if="!filterTrylater" v-if="!filterTrylater"
class="ico" class="ico"
:text="recipe.tried ? icon.try : icon.tried" :text="recipe.tried ? icon.try : icon.tried"
@tap="toggle('tried')" @tap="toggle('tried')"
/> />
<Button <Button
col="3" col="2"
v-else v-else
class="ico" class="ico"
:text="icon.done" :text="icon.done"
@tap="toggle('tried', true)" @tap="toggle('tried', 1)"
/> />
<Button <Button
col="4" col="3"
class="ico" class="ico"
:text="recipe.favorite ? icon.faved : icon.fav" :text="recipe.favorite ? icon.faved : icon.fav"
@tap="toggle('favorite')" @tap="toggle('favorite')"
/> />
<Button <Button
col="5" col="4"
v-if="!busy" :hidden="busyEdit"
class="ico" class="ico"
:text="icon.edit" :text="icon.edit"
@tap="editRecipe" @tap="editRecipe"
/> />
<ActivityIndicator col="5" v-else :busy="busy" /> <ActivityIndicator col="4" :hidden="!busyEdit" :busy="busyEdit" />
<Button <Button
col="6" col="5"
class="ico fab" class="ico fab"
:text="icon.share" :text="icon.share"
@tap="shareHandler" @tap="shareHandler"
/> />
</GridLayout> </RGridLayout>
<Toast :toast="toast" :action="hideLastTried" /> <Toast
<AbsoluteLayout rowSpan="2"> row="2"
colSpan="3"
:onload="tbLoad"
:toast="toast"
:action="hideBar"
/>
<AbsoluteLayout rowSpan="3" colSpan="3">
<Image <Image
@tap="({ object }) => (object.cancel = true)" @tap="closePhoto"
backgroundColor="black" backgroundColor="black"
stretch="aspectFit" stretch="aspectFit"
@loaded="onImgViewLoad" @loaded="onImgViewLoad"
@ -243,6 +265,7 @@
class="photoviewer" class="photoviewer"
/> />
</AbsoluteLayout> </AbsoluteLayout>
<WebView @loaded="wvLoad" hidden />
</GridLayout> </GridLayout>
</Page> </Page>
</template> </template>
@ -255,13 +278,13 @@ import {
Utils, Utils,
Span, Span,
FormattedString, FormattedString,
Label,
Observable, Observable,
Screen, Screen,
CoreTypes, CoreTypes,
WebView,
} from "@nativescript/core"; } from "@nativescript/core";
import { RLabel } from "~/rtl-ui";
import { localize } from "@nativescript/localize"; import { localize } from "@nativescript/localize";
const intl = require("nativescript-intl");
import { mapActions, mapState } from "vuex"; import { mapActions, mapState } from "vuex";
import CookingTimer from "./CookingTimer.vue"; import CookingTimer from "./CookingTimer.vue";
import EditRecipe from "./EditRecipe.vue"; import EditRecipe from "./EditRecipe.vue";
@ -269,17 +292,24 @@ import Action from "./modals/Action.vue";
import Toast from "./sub/Toast.vue"; import Toast from "./sub/Toast.vue";
import Prompt from "./modals/Prompt.vue"; import Prompt from "./modals/Prompt.vue";
import * as utils from "~/shared/utils"; import * as utils from "~/shared/utils";
const Intl = require("nativescript-intl");
let barTimer;
declare const android: any;
export default { export default {
components: { Toast }, components: { Toast },
props: ["filterTrylater", "recipeID"], props: ["filterTrylater", "recipeID", "yieldQuantity"],
data() { data() {
return { return {
busy: false, busyEdit: 0,
busyDup: 0,
yieldMultiplier: 1, yieldMultiplier: 1,
recipe: null, recipe: null,
currentRecipeID: this.recipeID, currentRecipeID: this.recipeID,
scrollview: null, scrollview: null,
appbar: null, appbar: null,
sidebar: null,
toastbar: null,
ingcon: null, ingcon: null,
inscon: null, inscon: null,
cmbcon: null, cmbcon: null,
@ -290,13 +320,15 @@ export default {
checked: 0, checked: 0,
stepsDid: 0, stepsDid: 0,
toast: null, toast: null,
photoOpen: false, photoOpen: 0,
showTitleArr: [false, false, false, false], showTitleArr: [0, 0, 0, 0],
sticky: null, sticky: null,
view: null,
wv: null,
}; };
}, },
computed: { computed: {
...mapState(["icon", "recipes"]), ...mapState(["icon", "recipes", "RTL"]),
tempYieldQuantity() { tempYieldQuantity() {
return Math.abs(this.yieldMultiplier) > 0 return Math.abs(this.yieldMultiplier) > 0
? Math.abs(parseFloat(this.yieldMultiplier)) ? Math.abs(parseFloat(this.yieldMultiplier))
@ -320,29 +352,38 @@ export default {
} }
return val; return val;
}, },
hasPrinterSupport() {
return utils.Printer.isSupported();
},
}, },
methods: { methods: {
...mapActions([ ...mapActions(["toggleStateAction", "setRatingAction", "toggleCartAction"]),
"toggleStateAction", pgLoad({ object }) {
"setComponent", this.busyDup = this.busyEdit = this.photoOpen = 0;
"setRatingAction",
"toggleCartAction",
]),
onPageLoad({ object }) {
object.bindingContext = new Observable(); object.bindingContext = new Observable();
this.busy = this.photoOpen = false;
this.setComponent("ViewRecipe");
if (this.yieldMultiplier == this.recipe.yieldQuantity) if (this.yieldMultiplier == this.recipe.yieldQuantity)
this.yieldMultiplier = this.recipe.yieldQuantity; this.yieldMultiplier = this.recipe.yieldQuantity;
this.keepScreenOn(true); utils.keepScreenOn(1);
this.syncCombinations(); this.syncCombinations();
this.view = object.page.getViewById("printview");
}, },
onPageUnload() { onPageUnload() {
this.keepScreenOn(false); utils.keepScreenOn(0);
}, },
onAppBarLoad({ object }) { sbload({ object }) {
this.sidebar = object;
},
abLoad({ object }) {
this.appbar = object; this.appbar = object;
}, },
tbLoad({ object }) {
this.toastbar = object;
this.recipe.tried && this.recipe.lastTried && this.showLastTried();
},
wvLoad({ object }) {
this.wv = object;
utils.updateLocale();
},
onIngsLoad({ object }) { onIngsLoad({ object }) {
this.ingcon = object; this.ingcon = object;
}, },
@ -366,24 +407,22 @@ export default {
this.imgView = object; this.imgView = object;
this.imgView.visibility = "collapsed"; this.imgView.visibility = "collapsed";
this.imgView.top = 24; this.imgView.top = 24;
this.imgView.left = Screen.mainScreen.widthDIPs - 112; this.imgView.left = this.RTL ? 16 : Screen.mainScreen.widthDIPs - 112;
}, },
onStickyLoad({ object }) { onStickyLoad({ object }) {
this.sticky = object; this.sticky = object;
}, },
fixTitle({ object }, swipeUp: boolean): void { fixTitle(object, swipeUp: boolean): void {
let ingL = this.recipe.ingredients.length; let ingL = this.recipe.ingredients.length;
let insL = this.recipe.instructions.length; let insL = this.recipe.instructions.length;
let cmbL = this.recipe.combinations.length; let cmbL = this.recipe.combinations.length;
let notL = this.recipe.notes.length; let notL = this.recipe.notes.length;
const isTop = (label): boolean => { const isTop = (label): boolean => {
let pos = label.getLocationRelativeTo(object).y; let pos = label.getLocationRelativeTo(object).y;
return label === this.cmbcon || label === this.notesT return label === this.cmbcon || label === this.notesT
? pos < 0 ? pos < 0
: pos + 32 < 0; : pos + 32 < 0;
}; };
const setVisibleTitle = (n: number): void => { const setVisibleTitle = (n: number): void => {
let arr = [ingL, insL, cmbL, notL]; let arr = [ingL, insL, cmbL, notL];
this.showTitleArr = Array.from( this.showTitleArr = Array.from(
@ -391,7 +430,6 @@ export default {
(v, i) => (v = arr[i] ? i < n : false) (v, i) => (v = arr[i] ? i < n : false)
); );
}; };
if (swipeUp) { if (swipeUp) {
if (ingL && !this.showTitleArr[0] && isTop(this.ingcon)) if (ingL && !this.showTitleArr[0] && isTop(this.ingcon))
setVisibleTitle(1); setVisibleTitle(1);
@ -412,45 +450,71 @@ export default {
setVisibleTitle(0); setVisibleTitle(0);
} }
}, },
onScroll(args) { svScroll({ object, scrollY }) {
let swipeUp: boolean; let swipeUp: boolean;
let y = args.scrollY; let y = scrollY;
if (y) { if (y) {
swipeUp = y > this.scrollPos; swipeUp = y > this.scrollPos;
this.scrollPos = Math.abs(y); this.scrollPos = Math.abs(y);
this.fixTitle(args, swipeUp); this.fixTitle(object, swipeUp);
if (!this.toast) { if (!this.toast) {
let ab = this.appbar.translateY; let ab = this.appbar.translateY;
if (swipeUp && ab == 0) if (swipeUp && ab == 0) this.hideBars();
this.appbar.animate({ else if (!swipeUp && ab == 64) this.showBars();
translate: { x: 0, y: 64 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
});
else if (!swipeUp && ab == 64)
this.appbar.animate({
translate: { x: 0, y: 0 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
});
} }
} }
}, },
// HELPERS showBars() {
this.appbar.animate({
translate: { x: 0, y: 0 },
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
});
this.sidebar.animate({
translate: { x: 0, y: 0 },
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
});
},
hideBars() {
this.appbar.animate({
translate: { x: 0, y: 64 },
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
});
this.sidebar.animate({
translate: { x: this.RTL ? -64 : 64, y: 0 },
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
});
},
// Helpers
getTitleCount(title, type) { getTitleCount(title, type) {
let count = this.recipe[type].length; let c = this.recipe[type].length;
let selected = null; let s = null;
switch (title) { switch (title) {
case "ings": case "ings":
selected = this.checked; s = this.checked;
break; break;
case "inss": case "inss":
selected = this.stepsDid; s = this.stepsDid;
break; break;
} }
let text = selected ? ` (${selected}/${count})` : ` (${count})`; c = this.getLocaleN(c);
s = s && this.getLocaleN(s);
let text = s ? ` (${s}/${c})` : ` (${c})`;
return localize(title) + text; return localize(title) + text;
}, },
getIngredientItem(item) {
return `${
this.roundedQuantity(item.quantity)
? this.roundedQuantity(item.quantity) + " "
: ""
}${this.roundedQuantity(item.quantity) ? localize(item.unit) + " " : ""}${
item.item
}`;
},
changeYield() { changeYield() {
this.$showModal(Prompt, { this.$showModal(Prompt, {
props: { props: {
@ -485,30 +549,24 @@ export default {
); );
}, },
showLastTried() { showLastTried() {
this.toast = localize("triedInfo", this.niceDate(this.recipe.lastTried)); this.animateBar(this.appbar, 0).then(() => {
utils.timer(10, (val) => { this.toast = localize(
if (!val) this.toast = val; "triedInfo",
this.niceDate(this.recipe.lastTried)
);
this.animateBar(this.toastbar, 1);
let a = 10;
clearInterval(barTimer);
barTimer = setInterval(() => a-- < 1 && this.hideBar(), 1000);
}); });
}, },
hideLastTried({ object }) { hideBar() {
object clearInterval(barTimer);
.animate({ this.animateBar(this.toastbar, 0).then(() => {
opacity: 0,
translate: { x: 0, y: 64 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
})
.then(() => {
this.showUndo = false;
this.appbar.translateY = 64;
this.appbar.animate({
translate: { x: 0, y: 0 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
});
object.opacity = 1;
object.translateY = 0;
this.toast = null; this.toast = null;
this.photoOpen
? (this.appbar.opacity = 1)
: this.animateBar(this.appbar, 1);
}); });
}, },
// getMeasure(value: number, unit: string) { // getMeasure(value: number, unit: string) {
@ -560,14 +618,6 @@ export default {
) / 100 ) / 100
); );
}, },
keepScreenOn(boolean) {
let activity =
Application.android.foregroundActivity ||
Application.android.startActivity;
let window = activity.getWindow();
let flag = android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
boolean ? window.addFlags(flag) : window.clearFlags(flag);
},
formattedTime(time) { formattedTime(time) {
let t = time.split(":"); let t = time.split(":");
let h = parseInt(t[0]); let h = parseInt(t[0]);
@ -577,15 +627,13 @@ export default {
return h ? (m ? `${h} ${hr} ${m} ${min}` : `${h} ${hr}`) : `${m} ${min}`; return h ? (m ? `${h} ${hr} ${m} ${min}` : `${h} ${hr}`) : `${m} ${min}`;
}, },
formattedDate(date) { formattedDate(date) {
let d = new Date(date); return new Intl.DateTimeFormat(null, {
var dateFormat = new intl.DateTimeFormat(null, {
year: "numeric", year: "numeric",
month: "long", month: "long",
day: "numeric", day: "numeric",
hour: "numeric", hour: "numeric",
minute: "numeric", minute: "numeric",
}).format(d); }).format(new Date(date));
return `${dateFormat}`;
}, },
isValidURL(string) { isValidURL(string) {
let pattern = new RegExp("^https?|^www", "ig"); let pattern = new RegExp("^https?|^www", "ig");
@ -654,26 +702,38 @@ export default {
this.inscon.getChildAt(i).className = "instruction"; this.inscon.getChildAt(i).className = "instruction";
} }
}, },
getDates() {
let u = `${localize("Last updated")}: ${this.formattedDate(
this.recipe.lastModified
)}`;
let c = `${localize("Created")}: ${this.formattedDate(
this.recipe.created
)}`;
return {
u,
c,
uc: u + "\n" + c,
};
},
// NAVIGATION HANDLERS // NavigationHandlers
editRecipe() { editRecipe() {
this.busy = true; this.busyEdit = 1;
this.$navigateTo(EditRecipe, { this.$navigateTo(EditRecipe, {
props: { props: {
navigationFromView: true,
filterTrylater: this.filterTrylater, filterTrylater: this.filterTrylater,
recipeID: this.currentRecipeID, recipeID: this.currentRecipeID,
}, },
// backstackVisible: false, animated: false,
}); });
}, },
viewCombination(combination) { viewCombination(combination) {
this.scrollview.scrollToVerticalOffset(0, true); this.scrollview.scrollToVerticalOffset(0, true);
this.recipe = this.recipes.filter((e) => e.id === combination)[0]; this.recipe = this.recipes.filter((e) => e.id === combination)[0];
this.showTitleArr = new Array(4).fill(false); this.showTitleArr = new Array(4).fill(0);
this.clearChecks(); this.clearChecks();
this.clearSteps(); this.clearSteps();
this.recipe.ingredients.forEach(() => this.checks.push(false)); this.recipe.ingredients.forEach(() => this.checks.push(0));
this.currentRecipeID = combination; this.currentRecipeID = combination;
this.syncCombinations(); this.syncCombinations();
this.createNotes(); this.createNotes();
@ -681,7 +741,7 @@ export default {
this.recipe.tried && this.recipe.lastTried && this.showLastTried(); this.recipe.tried && this.recipe.lastTried && this.showLastTried();
}, },
// SHARE ACTION // ShareAction
shareHandler() { shareHandler() {
if (this.recipe.image) { if (this.recipe.image) {
this.$showModal(Action, { this.$showModal(Action, {
@ -696,7 +756,7 @@ export default {
break; break;
case "pht": case "pht":
ImageSource.fromFile(this.recipe.image).then((res) => ImageSource.fromFile(this.recipe.image).then((res) =>
utils.shareImage(res, localize("srpu")) utils.shareImage(res, localize("srpu"), this.recipe.title)
); );
break; break;
} }
@ -769,7 +829,7 @@ export default {
utils.shareText(shareContent, localize("sru")); utils.shareText(shareContent, localize("sru"));
}, },
// DATA HANDLERS // DataHandlers
toggle(key: string, setDate: boolean) { toggle(key: string, setDate: boolean) {
this.toggleStateAction({ this.toggleStateAction({
id: this.currentRecipeID, id: this.currentRecipeID,
@ -787,7 +847,7 @@ export default {
} }
}, },
// SHOPPINGLIST // ShoppingList
toggleCart() { toggleCart() {
if (!this.recipe.inBag) { if (!this.recipe.inBag) {
} else { } else {
@ -797,10 +857,10 @@ export default {
}); });
}, },
// NOTES // Notes
createNote(note) { createNote(note) {
let regex = /(https?:\/\/[^\s]+)/g; let regex = /(https?:\/\/[^\s]+)/g;
const lbl = new Label(); const lbl = new RLabel();
lbl.className = "note"; lbl.className = "note";
lbl.textWrap = true; lbl.textWrap = true;
let fString = new FormattedString(); let fString = new FormattedString();
@ -851,7 +911,8 @@ export default {
} else this.$navigateBack(); } else this.$navigateBack();
}, },
viewPhoto() { viewPhoto() {
this.photoOpen = true; this.hideBars();
this.photoOpen = 1;
this.hijackBackEvent(); this.hijackBackEvent();
let pv = this.imgView; let pv = this.imgView;
pv.visibility = "visible"; pv.visibility = "visible";
@ -865,16 +926,16 @@ export default {
pv.animate({ pv.animate({
width: sw, width: sw,
height: sw, height: sw,
translate: { x: 112 - sw, y: (sh - sw) / 3 }, translate: { x: this.RTL ? -16 : 112 - sw, y: (sh - sw) / 3 },
duration: 250, duration: 200,
curve: CoreTypes.AnimationCurve.ease, curve: CoreTypes.AnimationCurve.ease,
}) })
) )
.then(() => .then(() =>
pv.animate({ pv.animate({
height: sh, height: sh,
translate: { x: -sw + 112, y: -((sh - sw) / 6) }, translate: { x: this.RTL ? -16 : 112 - sw, y: -((sh - sw) / 6) },
duration: 250, duration: 200,
curve: CoreTypes.AnimationCurve.ease, curve: CoreTypes.AnimationCurve.ease,
}) })
); );
@ -886,8 +947,8 @@ export default {
pv.animate({ pv.animate({
width: sw, width: sw,
height: sw, height: sw,
translate: { x: 112 - sw, y: (sh - sw) / 3 }, translate: { x: this.RTL ? -16 : 112 - sw, y: (sh - sw) / 3 },
duration: 250, duration: 200,
curve: CoreTypes.AnimationCurve.ease, curve: CoreTypes.AnimationCurve.ease,
}) })
.then(() => .then(() =>
@ -895,7 +956,7 @@ export default {
width: 96, width: 96,
height: 96, height: 96,
translate: { x: 0, y: 0 }, translate: { x: 0, y: 0 },
duration: 250, duration: 200,
curve: CoreTypes.AnimationCurve.ease, curve: CoreTypes.AnimationCurve.ease,
}) })
) )
@ -907,37 +968,162 @@ export default {
) )
.then(() => { .then(() => {
pv.visibility = "collapsed"; pv.visibility = "collapsed";
this.photoOpen = false; this.photoOpen = 0;
this.releaseBackEvent(); this.releaseBackEvent();
this.showBars();
}); });
}, },
navigateBack() {
this.photoOpen ? this.closePhoto() : this.$navigateBack();
},
//TIMERS // Timers
openCookingTimer() { openCookingTimer() {
this.$navigateTo(CookingTimer, { this.$navigateTo(CookingTimer, {
props: { props: {
recipeID: this.recipe.id, recipeID: this.recipe.id,
}, },
animated: false,
}); });
}, },
//HELPERS
//DuplicateRecipe
duplicateRecipe() {
this.busyDup = 1;
let dupRecipe = Object.assign({}, this.recipe);
dupRecipe.id = utils.getRandomID(0);
dupRecipe.title = dupRecipe.title + " " + localize("cpy");
this.$navigateTo(EditRecipe, {
props: {
dupRecipe,
},
animated: false,
});
},
// Print
prepareHTML() {
let r = this.recipe;
const head = `<head><meta charset=UTF-8><meta content="IE=edge"http-equiv=X-UA-Compatible><meta content="width=device-width,initial-scale=1"name=viewport><title>EnRecipes - Recipe for Print</title><style>a,body,div,html,img,ol,p,span,ul{border:0;font-size:100%;font:inherit;margin:0;padding:0;vertical-align:baseline}@font-face{font-family:Inter-Medium;src:url(../app/fonts/Inter-Medium.otf)}@font-face{font-family:Inter-Bold;src:url(../app/fonts/Inter-Bold.otf)}body{font-family:Inter-Medium,sans-serif;line-height:1.5;max-width:45rem;padding:1.5rem}body>p{padding:.5rem 0}.attr>div>p:last-child,h1,h2{font-family:Inter-Bold,sans-serif}#header{display:grid;grid-column-gap:2rem;grid-template-columns:1fr auto;margin-bottom:2.5rem;width:100%}img{border-radius:1rem;height:8rem;object-fit:cover;width:8rem}h1{font-size:2.25rem;line-height:1.25;margin:0;padding-bottom:1rem}svg{width:2rem;height:2rem;padding:0 .5rem 0 0}h2{margin:2rem 0 1rem}.attr{display:grid;grid-column-gap:2rem;grid-template-columns:1fr 1fr;margin-top:1rem}.attr>div>p:first-child{font-size:.9rem;opacity:.5}ol,ul{padding:0 1.5rem}li{padding:.5rem}a{color:inherit}.sub{font-size:.9rem;margin-top:2rem;opacity:.5}</style></head>`;
const getStarRating = () => {
let rate = `<svg width="100%" height="100%" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><path d="M10.756 2.826 8.419 7.98l-5.624.63c-.533.06-.982.425-1.147.935-.166.51-.018 1.07.378 1.431l4.179 3.816-1.138 5.543c-.108.525.101 1.065.535 1.38.434.315 1.012.348 1.478.083L12 19.002l4.92 2.796c.466.265 1.044.232 1.478-.083.434-.315.643-.855.535-1.38l-1.138-5.543 4.179-3.816c.396-.361.544-.921.378-1.431-.165-.51-.614-.875-1.147-.935l-5.624-.63-2.337-5.154c-.221-.489-.708-.802-1.244-.802s-1.023.313-1.244.802z"/></svg>`;
let unrate = `<svg width="100%" height="100%" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><path d="M10.756 2.826 8.419 7.98l-5.624.63c-.533.06-.982.425-1.147.935-.166.51-.018 1.07.378 1.431l4.179 3.816-1.138 5.543c-.108.525.101 1.065.535 1.38.434.315 1.012.348 1.478.083L12 19.002l4.92 2.796c.466.265 1.044.232 1.478-.083.434-.315.643-.855.535-1.38l-1.138-5.543 4.179-3.816c.396-.361.544-.921.378-1.431-.165-.51-.614-.875-1.147-.935l-5.624-.63-2.337-5.154c-.221-.489-.708-.802-1.244-.802s-1.023.313-1.244.802zM12 4.925l1.994 4.398c.146.321.45.542.8.581l4.799.538-3.567 3.256c-.26.237-.376.594-.305.94l.972 4.73-4.199-2.386c-.306-.174-.682-.174-.988.0l-4.199 2.386.972-4.73c.071-.346-.045-.703-.305-.94l-3.567-3.256 4.799-.538c.35-.039.654-.26.8-.581L12 4.925z"/></svg>`;
return rate.repeat(r.rating) + unrate.repeat(5 - r.rating);
};
const img = r.image ? `<img src="${r.image}" alt="${r.title}" />` : "";
const getIngs = () => {
let ing = [];
r.ingredients.forEach((e) => {
ing.push(`<li>${this.getIngredientItem(e)}</li>`);
});
return ing.join("");
};
const getIns = () => {
let ins = [];
r.instructions.forEach((e) => {
ins.push(`<li>${e}</li>`);
});
return ins.join("");
};
const getCmbs = () => {
let cmb = [];
r.combinations.forEach((e) => {
cmb.push(`<p>${this.getCombinationTitle(e)}</p>`);
});
return cmb.join("");
};
const getNotes = () => {
let regex = /(https?:\/\/[^\s]+)/g;
let n = [];
const createSpan = (val, isUrl) => {
return isUrl
? `<a href="${val}" target="_blank" rel="noopener noreferrer">${val}</a>`
: val;
};
r.notes.forEach((e) => {
let arr = e.split(regex);
let single = [];
arr.forEach((f) => {
single.push(createSpan(f, regex.test(f)));
});
n.push(`<p>${single.join("")}</p>`);
});
return n.join("");
};
return `<html dir="${
this.RTL ? "rtl" : "ltr"
}">${head}<body><div id=header><div class=title><h1>${
r.title
}</h1><div class=rating>${getStarRating()}</div></div>${img}</div><div class=attr><div><p>${localize(
"cui"
)}<p>${r.cuisine}</div><div><p>${localize("cat")}<p>${
r.category
}</div></div>${
r.tags.length
? `<div class=attr><div style="grid-column:span 2"><p>${localize(
"ts"
)}<p>${this.getTags(r.tags)}</div></div>`
: ""
} ${
this.hasTime(r.prepTime) || this.hasTime(r.cookTime)
? `<div class=attr>${
this.hasTime(r.prepTime)
? `<div><p>${localize("prepT")}<p>${this.formattedTime(
r.prepTime
)}</div>`
: ""
} ${
this.hasTime(r.cookTime)
? `<div><p>${localize("cookT")}<p>${this.formattedTime(
r.cookTime
)}</div>`
: ""
}</div>`
: ""
}<div class=attr><div><p>${localize("yld")}<p>${
this.tempYieldQuantity
} ${localize(r.yieldUnit)}</div><div><p>${localize(
"Difficulty level"
)}<p>${r.difficulty}</div></div>${
r.ingredients.length
? `<h2>${this.getTitleCount("ings", "ingredients")}</h2>`
: ""
}<ul>${getIngs()}</ul>${
r.instructions.length
? `<h2>${this.getTitleCount("inss", "instructions")}</h2>`
: ""
}<ol>${getIns()}</ol>${
r.combinations.length
? `<h2>${this.getTitleCount("cmbs", "combinations")}</h2>`
: ""
} ${getCmbs()} ${
r.notes.length ? `<h2>${this.getTitleCount("nos", "notes")}</h2>` : ""
} ${getNotes()}<div class=sub><p>${this.getDates().u}<p>${
this.getDates().c
}</div>
</body>
</html>`;
},
printView() {
let wv = this.wv as WebView;
const fileName = `${this.recipe.title} - ${localize("EnRecipes")}`;
wv.src = this.prepareHTML();
wv.once("loadFinished", () =>
utils.Printer.print(wv, fileName).then(() => (wv.src = null))
);
},
// Helpers
touchYield({ object, action }) { touchYield({ object, action }) {
object.className = action.match(/down|move/) object.className = action.match(/down|move/)
? "value clickable fade" ? "value accent fade"
: "value clickable"; : "value accent";
if (action == "up") this.changeYield(); if (action == "up") this.changeYield();
}, },
}, },
created() { created() {
this.recipe = this.recipes.filter((e) => e.id === this.currentRecipeID)[0]; this.recipe = this.recipes.filter((e) => e.id === this.currentRecipeID)[0];
this.recipe.ingredients.forEach((e) => this.checks.push(false)); this.recipe.ingredients.forEach((e) => this.checks.push(0));
}, },
mounted() { mounted() {
this.yieldMultiplier = this.recipe.yieldQuantity; this.yieldMultiplier = this.yieldQuantity || this.recipe.yieldQuantity;
this.recipe.tried && this.recipe.lastTried && this.showLastTried();
}, },
}; };
</script> </script>

View file

@ -2,14 +2,14 @@
<Page <Page
@loaded="transparentPage" @loaded="transparentPage"
backgroundColor="transparent" backgroundColor="transparent"
:class="appTheme" :class="theme"
> >
<GridLayout <GridLayout
columns="*" columns="*"
:rows="`auto, auto, ${stretch ? '*' : 'auto'}, auto`" :rows="`auto, auto, ${stretch ? '*' : 'auto'}, auto`"
class="modal" class="modal"
> >
<Label class="title" :text="title | L" /> <RLabel class="title" :text="title | L" />
<ListView <ListView
rowHeight="48" rowHeight="48"
row="2" row="2"
@ -17,10 +17,9 @@
:height="stretch ? '100%' : listHeight" :height="stretch ? '100%' : listHeight"
> >
<v-template> <v-template>
<Label <RLabel
class="listItem" class="listItem"
:class="{ tb: title === 'srt' && sortType === item }" :class="{ select: item == selected || $index == selected }"
:color="title === 'srt' && sortType === item ? '#ff5200' : ''"
:text="`${localized(item)}`" :text="`${localized(item)}`"
@touch="touch" @touch="touch"
@tap="tapAction(item)" @tap="tapAction(item)"
@ -28,9 +27,9 @@
/> />
</v-template> </v-template>
</ListView> </ListView>
<GridLayout row="3" columns="auto, *, auto" class="actions"> <RGridLayout :rtl="RTL" row="3" columns="auto, *, auto" class="actions">
<Button <Button
v-if="action" :hidden="!action"
class="text sm" class="text sm"
:text="action | L" :text="action | L"
@tap="$modal.close(action)" @tap="$modal.close(action)"
@ -39,9 +38,9 @@
col="2" col="2"
class="text sm" class="text sm"
:text="'cBtn' | L" :text="'cBtn' | L"
@tap="$modal.close(false)" @tap="$modal.close(0)"
/> />
</GridLayout> </RGridLayout>
</GridLayout> </GridLayout>
</Page> </Page>
</template> </template>
@ -54,7 +53,7 @@ import Confirm from "./Confirm.vue";
interface IData { interface IData {
newList: unknown[]; newList: unknown[];
stretch: boolean; stretch: number;
listHeight: number; listHeight: number;
} }
@ -72,26 +71,30 @@ export default {
type: String, type: String,
required: false, required: false,
}, },
selected: {
type: String,
required: false,
},
}, },
data(): IData { data(): IData {
return { return {
newList: this.list, newList: this.list,
stretch: false, stretch: 0,
listHeight: 0, listHeight: 0,
}; };
}, },
computed: { computed: {
...mapState(["sortType", "icon", "appTheme"]), ...mapState(["sortType", "icon", "theme", "RTL"]),
}, },
methods: { methods: {
...mapActions(["removeListItemAction"]), ...mapActions(["removeListItemAction", "deleteTimerPreset"]),
localized(item: string): string { localized(item: string): string {
return this.title !== "lang" ? localize(item) : item; return this.title !== "lang" ? localize(item) : item;
}, },
tapAction(item: string): void { tapAction(item: string): void {
this.$modal.close(item); this.$modal.close(item);
}, },
deletionConfirmation(description: string): void { removeConfirmation(description: string): void {
return this.$showModal(Confirm, { return this.$showModal(Confirm, {
props: { props: {
title: "conf", title: "conf",
@ -101,13 +104,24 @@ export default {
}, },
}); });
}, },
deletionConfirmation(description: string): void {
return this.$showModal(Confirm, {
props: {
title: "conf",
description,
cancelButtonText: "cBtn",
okButtonText: "dBtn",
},
});
},
removeItem(item: string): void { removeItem(item: string): void {
let vm = this; let vm = this;
let index = this.newList.findIndex((e) => e === item);
let localizedItem = `"${localize(item)}"`; let localizedItem = `"${localize(item)}"`;
function removeListItem(listName: string, desc: string): void { function removeListItem(listName: string, desc: string): void {
vm.deletionConfirmation(`${localize(desc, localizedItem)}`).then( vm.removeConfirmation(`${localize(desc, localizedItem)}`).then(
(action: boolean) => { (action: boolean) => {
if (action != null && action) if (action)
vm.removeListItemAction({ vm.removeListItemAction({
item, item,
listName, listName,
@ -115,6 +129,16 @@ export default {
} }
); );
} }
function deleteTimerPreset(): void {
vm.deletionConfirmation(`${localize("delPrst", `"${item}"`)}`).then(
(action: boolean) => {
if (action) {
vm.deleteTimerPreset(index);
vm.newList.splice(index, 1);
}
}
);
}
switch (this.title) { switch (this.title) {
case "cui": case "cui":
removeListItem("cuisines", "rmCuiInfo"); removeListItem("cuisines", "rmCuiInfo");
@ -128,12 +152,18 @@ export default {
case "Unit": case "Unit":
removeListItem("units", "rmUInfo"); removeListItem("units", "rmUInfo");
break; break;
case "prsts":
deleteTimerPreset();
break;
} }
}, },
touch({ object, action }): void { touch({ object, action }): void {
let classes = object.className;
object.className = action.match(/down|move/) object.className = action.match(/down|move/)
? "listItem fade" ? !classes.includes("fade")
: "listItem"; ? classes + " fade"
: classes
: classes.replace(/ fade/g, "");
}, },
}, },
created() { created() {
@ -145,7 +175,7 @@ export default {
if (modalHeight < usableHeight) { if (modalHeight < usableHeight) {
this.listHeight = listHeight; this.listHeight = listHeight;
} else { } else {
this.stretch = true; this.stretch = 1;
} }
}, },
}; };

View file

@ -2,20 +2,25 @@
<Page <Page
@loaded="transparentPage" @loaded="transparentPage"
backgroundColor="transparent" backgroundColor="transparent"
:class="appTheme" :class="theme"
> >
<GridLayout columns="*" rows="auto, auto, *, auto" class="modal"> <GridLayout columns="*" rows="auto, auto, *, auto" class="modal">
<Label class="title" :text="title | L" /> <RLabel class="title" :text="title | L" />
<StackLayout <StackLayout
row="1" row="1"
v-if="filteredRecipes.length || searchQuery" :hidden="!filteredRecipes.length && !searchQuery"
class="input" class="input"
> >
<TextField class="modalInput" :hint="'ser' | L" v-model="searchQuery" /> <TextField
@loaded="setGravity"
class="modalInput"
:hint="'ser' | L"
v-model="searchQuery"
/>
</StackLayout> </StackLayout>
<ListView row="2" for="recipe in filteredRecipes"> <ListView row="2" for="recipe in filteredRecipes">
<v-template> <v-template>
<Label <RLabel
class="listItem" class="listItem"
:text="recipe.title" :text="recipe.title"
@touch="touch($event, recipe)" @touch="touch($event, recipe)"
@ -25,18 +30,18 @@
<Label <Label
row="2" row="2"
class="noResInfo" class="noResInfo"
v-if="!filteredRecipes.length && !searchQuery" :hidden="recipes.length"
:text="'recListEmp' | L" :text="'recListEmp' | L"
/> />
<Label <Label
row="2" row="2"
class="noResInfo" class="noResInfo"
v-if="!filteredRecipes.length && searchQuery" :hidden="filteredRecipes.length || !searchQuery"
:text="'noRecs' | L" :text="'noRecs' | L"
/> />
<GridLayout row="3" columns="auto, *, auto" class="actions"> <RGridLayout :rtl="RTL" row="3" columns="auto, *, auto" class="actions">
<Button <Button
v-if="action" :hidden="!action"
class="text sm" class="text sm"
:text="action | L" :text="action | L"
@tap="$modal.close(action)" @tap="$modal.close(action)"
@ -45,9 +50,9 @@
col="2" col="2"
class="text sm" class="text sm"
:text="'cBtn' | L" :text="'cBtn' | L"
@tap="$modal.close(false)" @tap="$modal.close(0)"
/> />
</GridLayout> </RGridLayout>
</GridLayout> </GridLayout>
</Page> </Page>
</template> </template>
@ -62,7 +67,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["icon", "appTheme"]), ...mapState(["icon", "theme", "RTL"]),
filteredRecipes() { filteredRecipes() {
return this.recipes return this.recipes
.map((e, i) => { .map((e, i) => {
@ -82,8 +87,8 @@ export default {
tapAction(recipe) { tapAction(recipe) {
this.$modal.close(recipe.id); this.$modal.close(recipe.id);
}, },
centerLabel(args) { centerLabel({ object }) {
args.object.android.setGravity(16); object.android.setGravity(16);
}, },
recipeFilter(e) { recipeFilter(e) {
let searchQuery = this.searchQuery.toLowerCase(); let searchQuery = this.searchQuery.toLowerCase();
@ -98,7 +103,7 @@ export default {
touch({ object, action }, recipe) { touch({ object, action }, recipe) {
object.className = action.match(/down|move/) object.className = action.match(/down|move/)
? "listItem fade" ? "listItem fade"
: "listItem"; : "listItem ";
if (action == "up") this.tapAction(recipe); if (action == "up") this.tapAction(recipe);
}, },
}, },

View file

@ -2,31 +2,31 @@
<Page <Page
@loaded="transparentPage" @loaded="transparentPage"
backgroundColor="transparent" backgroundColor="transparent"
:class="appTheme" :class="theme"
> >
<GridLayout rows="auto, auto, auto" class="modal"> <GridLayout rows="auto, auto, auto" class="modal">
<Label class="title" :text="title | L" /> <RLabel class="title" :text="title | L" />
<Label <Label
row="1" row="1"
v-if="description" v-if="description"
class="description tw" class="description tw"
:text="description" :text="description"
/> />
<GridLayout row="2" columns="*, auto, auto" class="actions"> <RGridLayout :rtl="RTL" row="2" columns="*, auto, auto" class="actions">
<Button <Button
v-if="cancelButtonText" v-if="cancelButtonText"
col="1" col="1"
class="text sm" class="text sm"
:text="cancelButtonText | L" :text="cancelButtonText | L"
@tap="$modal.close(false)" @tap="$modal.close(0)"
/> />
<Button <Button
col="2" col="2"
class="text sm" class="text sm"
:text="okButtonText | L" :text="okButtonText | L"
@tap="$modal.close(true)" @tap="$modal.close(1)"
/> />
</GridLayout> </RGridLayout>
</GridLayout> </GridLayout>
</Page> </Page>
</template> </template>
@ -53,7 +53,7 @@ export default {
}, },
}, },
computed: { computed: {
...mapState(["icon", "appTheme"]), ...mapState(["icon", "theme", "RTL"]),
}, },
}; };
</script> </script>

View file

@ -0,0 +1,108 @@
<template>
<Page
@loaded="transparentPage"
backgroundColor="transparent"
:class="theme"
>
<GridLayout rows="auto, auto, auto" class="modal">
<RLabel class="title" :text="title | L" />
<RStackLayout
:rtl="RTL"
row="1"
orientation="horizontal"
horizontalAlignment="center"
>
<ListPicker
@loaded="onLPLoad"
:items="days"
:selectedIndex="dIndex"
@selectedIndexChange="setD"
></ListPicker>
<ListPicker
@loaded="onLPLoad"
:items="months"
:selectedIndex="currentM"
@selectedIndexChange="setM"
></ListPicker>
<ListPicker
:items="years"
:selectedIndex="yIndex"
@selectedIndexChange="setY"
></ListPicker>
</RStackLayout>
<RGridLayout :rtl="RTL" row="2" columns="*, auto, auto" class="actions">
<Button
col="1"
class="text sm"
:text="'cBtn' | L"
@tap="$modal.close(0)"
/>
<Button
col="2"
class="text sm"
:text="'SET' | L"
@tap="$modal.close(selected)"
/>
</RGridLayout>
</GridLayout>
</Page>
</template>
<script>
import { mapState } from "vuex";
import { localize } from "@nativescript/localize";
export default {
props: ["title", "monthNames", "currentD", "currentM", "currentY"],
data() {
return {
selectedD: 0,
selectedM: 0,
selectedY: 0,
};
},
computed: {
...mapState(["icon", "theme", "RTL"]),
days() {
let ld = new Date(this.selectedY, this.selectedM + 1, 0).getDate();
return Array.from({ length: ld }, (v, i) => i + 1);
},
dIndex() {
return this.days.indexOf(this.selectedD || this.currentD);
},
months() {
return this.monthNames.map((e) => localize(e));
},
years() {
let min = 1900;
let max = min + 200;
let years = [];
for (let i = min; i <= max; i++) years.push(i);
return years;
},
yIndex() {
return this.years.indexOf(this.currentY);
},
selected() {
return {
date: this.selectedD,
month: this.selectedM,
year: this.selectedY,
};
},
},
methods: {
onLPLoad({ object }) {
object.android.setWrapSelectorWheel(true);
},
setD(args) {
this.selectedD = args.object.selectedIndex + 1;
},
setM(args) {
this.selectedM = args.object.selectedIndex;
},
setY(args) {
this.selectedY = this.years[args.object.selectedIndex];
},
},
};
</script>

View file

@ -1,28 +1,32 @@
<template> <template>
<Page @loaded="onPageLoad" backgroundColor="transparent" :class="appTheme"> <Page @loaded="pgLoad" backgroundColor="transparent" :class="theme">
<GridLayout rows="auto, auto, *, auto" columns="*" class="modal"> <GridLayout rows="auto, auto, *, auto" columns="*" class="modal">
<Label class="title" :text="`fltr` | L" /> <RLabel class="title a" :text="`fltr` | L" />
<ScrollView orientation="horizontal" row="1" @loaded="onScrollLoad"> <ScrollView orientation="horizontal" row="1" @loaded="onScrollLoad">
<StackLayout class="filters" orientation="horizontal"> <RStackLayout :rtl="RTL" class="filters" orientation="horizontal">
<GridLayout <GridLayout
rows="48" rows="48"
columns="auto, auto" columns="auto, auto"
class="segment" class="segment rtl"
v-for="(item, index) in pathList" v-for="(item, index) in pathList"
:key="index" :key="index"
:class="{ :class="{ select: filterType === item.type }"
select: filterType === item.type,
}"
@touch="touchSelector($event, item.type)" @touch="touchSelector($event, item.type)"
> >
<Label class="ico" :text="icon[item.type]" /> <Label class="ico" :text="icon[item.type]" />
<Label v-if="item.title" class="value" :text="item.title" col="1" />
</GridLayout>
</StackLayout>
</ScrollView>
<ListView row="2" class="options-list" for="item in filterList">
<v-template>
<Label <Label
:hidden="!item.title"
class="value"
:class="{ r: RTL }"
:text="item.title"
col="1"
/>
</GridLayout>
</RStackLayout>
</ScrollView>
<ListView row="2" class="options" for="item in filterList">
<v-template>
<RLabel
class="listItem" class="listItem"
verticalAlignment="center" verticalAlignment="center"
col="1" col="1"
@ -31,7 +35,12 @@
/> />
</v-template> </v-template>
</ListView> </ListView>
<GridLayout row="3" columns="auto, *, auto, auto" class="actions"> <RGridLayout
:rtl="RTL"
row="3"
columns="auto, *, auto, auto"
class="actions"
>
<Button class="text sm" :text="'rest' | L" @tap="resetFilter" /> <Button class="text sm" :text="'rest' | L" @tap="resetFilter" />
<Button <Button
col="2" col="2"
@ -45,7 +54,7 @@
:text="'apply' | L" :text="'apply' | L"
@tap="applyFilter" @tap="applyFilter"
/> />
</GridLayout> </RGridLayout>
</GridLayout> </GridLayout>
</Page> </Page>
</template> </template>
@ -61,7 +70,7 @@ export default {
localCuisine: null, localCuisine: null,
localCategory: null, localCategory: null,
localTag: null, localTag: null,
reset: false, reset: 0,
}; };
}, },
computed: { computed: {
@ -70,10 +79,11 @@ export default {
"recipes", "recipes",
"cuisines", "cuisines",
"categories", "categories",
"selectedCuisine", "selCuisine",
"selectedCategory", "selCategory",
"selectedTag", "selTag",
"appTheme", "theme",
"RTL",
]), ]),
pathList() { pathList() {
let arr = [ let arr = [
@ -163,18 +173,12 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions([ ...mapActions(["setCuisine", "setCategory", "setTag", "clearFilter"]),
"setComponent", pgLoad(args) {
"setCuisine",
"setCategory",
"setTag",
"clearFilter",
]),
onPageLoad(args) {
this.transparentPage(args); this.transparentPage(args);
this.localCuisine = this.selectedCuisine; this.localCuisine = this.selCuisine;
this.localCategory = this.selectedCategory; this.localCategory = this.selCategory;
this.localTag = this.selectedTag; this.localTag = this.selTag;
if (this.localCuisine) this.filterType = "category"; if (this.localCuisine) this.filterType = "category";
if (this.localCategory && this.localTag) this.filterType = "tag"; if (this.localCategory && this.localTag) this.filterType = "tag";
this.scrollToRight(); this.scrollToRight();
@ -198,14 +202,14 @@ export default {
setTimeout( setTimeout(
() => () =>
this.scrollview.scrollToHorizontalOffset( this.scrollview.scrollToHorizontalOffset(
this.scrollview.scrollableWidth, this.RTL ? 0 : this.scrollview.scrollableWidth,
true true
), ),
10 10
); );
}, },
setRecipeFilter(item) { setRecipeFilter(item) {
this.reset = false; this.reset = 0;
switch (this.filterType) { switch (this.filterType) {
case "cuisine": case "cuisine":
this.localCuisine = item; this.localCuisine = item;
@ -225,27 +229,26 @@ export default {
this.setCuisine(this.localCuisine); this.setCuisine(this.localCuisine);
this.setCategory(this.localCategory); this.setCategory(this.localCategory);
this.setTag(this.localTag); this.setTag(this.localTag);
this.filterFavourites = this.filterTrylater = false; this.filterFavourites = this.filterTrylater = 0;
if (this.reset) this.setComponent("EnRecipes"); this.$modal.close(this.reset);
else this.setComponent("Filtered recipes");
this.$modal.close();
}, },
resetFilter() { resetFilter() {
this.filterType = "cuisine"; this.filterType = "cuisine";
this.localCuisine = this.localCategory = this.localTag = null; this.localCuisine = this.localCategory = this.localTag = null;
this.reset = true; this.reset = 1;
}, },
touch({ object, action }, item) { touch({ object, action }, item) {
object.className = action.match(/down|move/) object.className = action.match(/down|move/)
? "listItem fade" ? "listItem fade"
: "listItem"; : "listItem ";
if (action == "up") this.setRecipeFilter(item); if (action == "up") this.setRecipeFilter(item);
}, },
touchSelector({ object, action }, type) { touchSelector({ object, action }, type) {
let selected = this.filterType == type; let selected = this.filterType == type;
let classes = `segment ${this.RTL ? "rtl" : ""} `;
object.className = action.match(/down|move/) object.className = action.match(/down|move/)
? `segment ${selected ? "select" : "fade"}` ? `${classes}${selected ? "select" : "fade"}`
: `segment ${selected && "select"}`; : `${classes}${selected && "select"}`;
if (action == "up") this.setFilterType(type); if (action == "up") this.setFilterType(type);
}, },
}, },

View file

@ -2,24 +2,35 @@
<Page <Page
@loaded="transparentPage" @loaded="transparentPage"
backgroundColor="transparent" backgroundColor="transparent"
:class="appTheme" :class="theme"
> >
<GridLayout rows="auto, auto, auto" class="modal"> <GridLayout rows="auto, auto, auto" class="modal">
<Label class="title" :text="title | L" /> <RLabel class="title" :text="title | L" />
<StackLayout row="1" class="input"> <StackLayout row="1" class="input">
<TextView
v-if="type == 'view'"
autocorrect="true"
autocapitalizationType="sentences"
class="modalInput"
@loaded="focusField"
v-model="text"
/>
<TextField <TextField
autocapitalizationType="sentences"
autocorrect="true"
v-else
class="modalInput" class="modalInput"
@loaded="focusField" @loaded="focusField"
v-model="text" v-model="text"
@returnPress="$modal.close(text)" @returnPress="$modal.close(text)"
/> />
</StackLayout> </StackLayout>
<GridLayout row="2" columns="*, auto, auto" class="actions"> <RGridLayout :rtl="RTL" row="2" columns="*, auto, auto" class="actions">
<Button <Button
col="1" col="1"
class="text sm" class="text sm"
:text="'cBtn' | L" :text="'cBtn' | L"
@tap="$modal.close(false)" @tap="$modal.close(0)"
/> />
<Button <Button
col="2" col="2"
@ -27,7 +38,7 @@
:text="action | L" :text="action | L"
@tap="$modal.close(text)" @tap="$modal.close(text)"
/> />
</GridLayout> </RGridLayout>
</GridLayout> </GridLayout>
</Page> </Page>
</template> </template>
@ -37,20 +48,23 @@ import { Utils } from "@nativescript/core";
import { localize } from "@nativescript/localize"; import { localize } from "@nativescript/localize";
import { mapState } from "vuex"; import { mapState } from "vuex";
export default { export default {
props: ["title", "hint", "placeholder", "action"], props: ["title", "type", "hint", "placeholder", "action"],
data() { data() {
return { return {
text: null, text: null,
}; };
}, },
computed: { computed: {
...mapState(["icon", "appTheme"]), ...mapState(["icon", "theme", "RTL"]),
}, },
methods: { methods: {
focusField({ object }) { focusField({ object }) {
this.setGravity(object);
let a = this.placeholder; let a = this.placeholder;
typeof a == "number" typeof a == "number"
? (object.keyboardType = "number") ? (object.keyboardType = "number")
: this.type
? ""
: (object.autocapitalizationType = "words"); : (object.autocapitalizationType = "words");
object.hint = this.hint; object.hint = this.hint;
object.focus(); object.focus();

View file

@ -2,45 +2,43 @@
<Page <Page
@loaded="transparentPage" @loaded="transparentPage"
backgroundColor="transparent" backgroundColor="transparent"
:class="appTheme" :class="theme"
> >
<GridLayout rows="auto, auto, auto" class="modal"> <GridLayout rows="auto, auto, auto" class="modal">
<Label class="title" :text="title | L" /> <RLabel class="title" :text="title | L" />
<StackLayout <RStackLayout
:rtl="RTL"
row="1" row="1"
class="dialogListPicker"
orientation="horizontal" orientation="horizontal"
horizontalAlignment="center" horizontalAlignment="center"
> >
<ListPicker <ListPicker
@loaded="onLPLoad" @loaded="onLPLoad"
ref="hrPicker"
:items="hrsList" :items="hrsList"
:selectedIndex="hrIndex" :selectedIndex="hrIndex"
@selectedIndexChange="setHrs" @selectedIndexChange="setHrs"
></ListPicker> ></ListPicker>
<ListPicker <ListPicker
@loaded="onLPLoad" @loaded="onLPLoad"
ref="minPicker"
:items="minsList" :items="minsList"
:selectedIndex="minIndex" :selectedIndex="minIndex"
@selectedIndexChange="setMins" @selectedIndexChange="setMins"
></ListPicker> ></ListPicker>
</StackLayout> </RStackLayout>
<GridLayout row="2" columns="*, auto, auto" class="actions"> <RGridLayout :rtl="RTL" row="2" columns="*, auto, auto" class="actions">
<Button <Button
col="1" col="1"
class="text sm" class="text sm"
:text="'cBtn' | L" :text="'cBtn' | L"
@tap="$modal.close(false)" @tap="$modal.close(0)"
/> />
<Button <Button
col="2" col="2"
class="text sm" class="text sm"
:text="action | L" :text="'SET' | L"
@tap="$modal.close(selectedTime)" @tap="$modal.close(selectedTime)"
/> />
</GridLayout> </RGridLayout>
</GridLayout> </GridLayout>
</Page> </Page>
</template> </template>
@ -49,7 +47,7 @@
import { mapState } from "vuex"; import { mapState } from "vuex";
import { localize } from "@nativescript/localize"; import { localize } from "@nativescript/localize";
export default { export default {
props: ["title", "selectedHr", "selectedMin", "action"], props: ["title", "selectedHr", "selectedMin"],
data() { data() {
return { return {
hrs: [], hrs: [],
@ -59,7 +57,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["icon", "appTheme"]), ...mapState(["icon", "theme", "RTL"]),
hrsList() { hrsList() {
let h = [...Array(24).keys()]; let h = [...Array(24).keys()];
this.hrs = h; this.hrs = h;

View file

@ -2,20 +2,25 @@
<Page <Page
@loaded="transparentPage" @loaded="transparentPage"
backgroundColor="transparent" backgroundColor="transparent"
:class="appTheme" :class="theme"
> >
<GridLayout rows="auto, auto, auto" class="modal"> <GridLayout rows="auto, auto, auto" class="modal">
<Label class="title" :text="title | L" /> <RLabel class="title" :text="title | L" />
<StackLayout row="1"> <StackLayout row="1">
<StackLayout class="input"> <StackLayout class="input">
<TextField <TextField
@loaded="setGravity"
class="modalInput" class="modalInput"
v-model="setLabel" v-model="setLabel"
:hint="label" :hint="label"
autocapitalizationType="words" autocapitalizationType="words"
autocorrect="true"
/></StackLayout> /></StackLayout>
<!-- @loaded="focusField" --> <RStackLayout
<StackLayout orientation="horizontal" horizontalAlignment="center"> :rtl="RTL"
orientation="horizontal"
horizontalAlignment="center"
>
<ListPicker <ListPicker
@loaded="onLPLoad" @loaded="onLPLoad"
:items="hrsList" :items="hrsList"
@ -31,10 +36,15 @@
:items="secsList" :items="secsList"
@selectedIndexChange="setSec" @selectedIndexChange="setSec"
></ListPicker> ></ListPicker>
</StackLayout> </RStackLayout>
</StackLayout> </StackLayout>
<GridLayout row="2" columns="auto, *, auto, auto" class="actions"> <RGridLayout
:rtl="RTL"
row="2"
columns="auto, *, auto, auto"
class="actions r"
>
<Button <Button
v-if="showPreset" v-if="showPreset"
class="text sm" class="text sm"
@ -45,10 +55,10 @@
col="2" col="2"
class="text sm" class="text sm"
:text="'cBtn' | L" :text="'cBtn' | L"
@tap="$modal.close(false)" @tap="$modal.close(0)"
/> />
<Button col="3" class="text sm" :text="action | L" @tap="sendRespose" /> <Button col="3" class="text sm" :text="action | L" @tap="sendRespose" />
</GridLayout> </RGridLayout>
</GridLayout> </GridLayout>
</Page> </Page>
</template> </template>
@ -71,7 +81,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["icon", "appTheme"]), ...mapState(["icon", "theme", "RTL"]),
hrsList() { hrsList() {
let h = [...Array(24).keys()]; let h = [...Array(24).keys()];
this.hrs = h; this.hrs = h;

View file

@ -0,0 +1,114 @@
<template>
<Page @loaded="pgLoad" :class="theme" backgroundColor="#ff5200">
<GridLayout rows="*, *">
<Button
class="ico fab"
fontSize="128"
:width="screenWidth"
:height="screenWidth"
:text="icon.timer"
/>
<GridLayout row="1" rows="*, auto" columns="*, auto, *">
<ScrollView :class="theme" rowSpan="2" colSpan="3">
<StackLayout paddingTop="8">
<Timer
v-for="timer in timers"
:key="timer.id"
:timer="timer"
:formattedTime="formattedTime"
:removeTimer="removeTimer"
:togglePause="togglePause"
:timerAlert="timerAlert"
:showToast="showToast"
/>
<StackLayout class="listSpace"> </StackLayout>
</StackLayout>
</ScrollView>
<GridLayout col="1" row="1" class="appbar">
<Button
class="ico fab"
margin="0"
:text="icon.x"
@tap="removeTimers"
col="1"
/>
</GridLayout>
</GridLayout>
</GridLayout>
</Page>
</template>
<script lang="ts">
import { Application, Screen, Device, Color, Utils } from "@nativescript/core";
import Timer from "../sub/Timer.vue";
import * as utils from "~/shared/utils";
import { mapState, mapActions } from "vuex";
const windowMgr = android.view.WindowManager;
const View = android.view.View as any;
const ViewGroup = android.view.ViewGroup;
export default {
components: { Timer },
props: [
"formattedTime",
"removeTimer",
"togglePause",
"timerAlert",
"showToast",
],
computed: {
...mapState(["icon", "theme", "activeTimers"]),
screenWidth() {
return Screen.mainScreen.widthDIPs;
},
timers() {
let timers = this.activeTimers.filter((e) => e.done);
if (!timers.length) {
this.$modal.close(1);
this.isScreenLocked && this.turnOffScreen();
}
return timers;
},
sdkv() {
return parseInt(Device.sdkVersion);
},
isScreenLocked() {
const keyguardMgr = Utils.ad
.getApplicationContext()
.getSystemService(android.content.Context.KEYGUARD_SERVICE);
return this.sdkv > 21
? keyguardMgr.isDeviceLocked()
: keyguardMgr.isKeyguardLocked();
},
},
methods: {
...mapActions(["clearTimerInterval"]),
pgLoad({ object }) {
let dialog = object._dialogFragment.getDialog();
let dialogWindow = dialog.getWindow();
let decorView = dialogWindow.getDecorView();
dialog.setCancelable(false);
if (this.isScreenLocked) {
const flags =
windowMgr.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
windowMgr.LayoutParams.FLAG_TURN_SCREEN_ON |
windowMgr.LayoutParams.FLAG_KEEP_SCREEN_ON;
dialogWindow.addFlags(flags);
}
utils.setBarColors(dialogWindow, decorView, this.theme);
dialogWindow.setStatusBarColor(new Color("#ff5200").android);
},
removeTimers() {
this.timers.forEach((timer) => this.removeTimer(timer.id, 1));
this.isScreenLocked && this.turnOffScreen();
},
turnOffScreen() {
const window = Application.android.startActivity.getWindow();
const flags =
windowMgr.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
windowMgr.LayoutParams.FLAG_TURN_SCREEN_ON |
windowMgr.LayoutParams.FLAG_KEEP_SCREEN_ON;
window.clearFlags(flags);
},
},
};
</script>

View file

@ -1,12 +1,7 @@
<template> <template>
<Page @loaded="onPageLoad" actionBarHidden="true"> <Page @loaded="pgLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *"> <RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<ListView <ListView rowSpan="2" colSpan="2" class="options" for="item in items">
rowSpan="2"
colSpan="2"
class="options-list"
for="item in items"
>
<v-template if="$index == 0"> <v-template if="$index == 0">
<Label class="pageTitle" :text="'About' | L" /> <Label class="pageTitle" :text="'About' | L" />
</v-template> </v-template>
@ -23,20 +18,26 @@
<StackLayout class="listSpace"> </StackLayout> <StackLayout class="listSpace"> </StackLayout>
</v-template> </v-template>
<v-template> <v-template>
<GridLayout <RGridLayout
:rtl="RTL"
columns="auto, *" columns="auto, *"
class="option" class="option"
@touch="touch($event, item.url)" @touch="touch($event, item.url)"
> >
<Label class="ico" :text="icon[item.icon]" /> <Label
<Label col="1" :text="item.title | L" /> class="ico"
</GridLayout> :class="{ rtl: /help|don/.test(item.icon) }"
:text="icon[item.icon]"
/>
<Label col="1" class="info" :text="item.title | L" />
</RGridLayout>
</v-template> </v-template>
</ListView> </ListView>
<GridLayout row="1" class="appbar" rows="*" columns="auto, *"> <GridLayout row="1" class="appbar rtl" rows="*" columns="auto, *">
<Button class="ico" :text="icon.back" @tap="$navigateBack()" /> <Button class="ico" :text="icon.back" @tap="$navigateBack()" />
</GridLayout> </GridLayout>
</GridLayout> </RGridLayout>
</Page> </Page>
</template> </template>
@ -46,7 +47,7 @@ import { mapState } from "vuex";
export default { export default {
computed: { computed: {
...mapState(["icon"]), ...mapState(["icon", "RTL"]),
items() { items() {
return [ return [
{}, {},
@ -69,8 +70,7 @@ export default {
{ {
icon: "priv", icon: "priv",
title: "priv", title: "priv",
url: url: "https://github.com/vishnuraghavb/EnRecipes/blob/main/PRIVACY.md",
"https://github.com/vishnuraghavb/EnRecipes/blob/main/PRIVACY.md",
}, },
{ {
icon: "don", icon: "don",
@ -95,7 +95,7 @@ export default {
}, },
}, },
methods: { methods: {
onPageLoad({ object }) { pgLoad({ object }) {
object.bindingContext = new Observable(); object.bindingContext = new Observable();
}, },
// HELPERS // HELPERS

View file

@ -1,17 +1,17 @@
<template> <template>
<Page @loaded="onPageLoad" actionBarHidden="true"> <Page @loaded="pgLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *"> <RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<OptionsList title="Settings" :items="items" :action="navigateTo" /> <OptionsList title="Settings" :items="items" :action="navigateTo" />
<GridLayout row="1" class="appbar" rows="*" columns="auto, *"> <GridLayout row="1" class="appbar rtl" rows="*" columns="auto, *">
<Button class="ico" :text="icon.back" @tap="$navigateBack()" /> <Button class="ico" :text="icon.back" @tap="$navigateBack()" />
</GridLayout> </GridLayout>
</GridLayout> </RGridLayout>
</Page> </Page>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Observable } from "@nativescript/core"; import { Observable } from "@nativescript/core";
import { mapState, mapActions } from "vuex"; import { mapState } from "vuex";
import Interface from "./Interface.vue"; import Interface from "./Interface.vue";
import Options from "./Options.vue"; import Options from "./Options.vue";
import Database from "./Database.vue"; import Database from "./Database.vue";
@ -27,30 +27,35 @@ export default {
{ {
type: "list", type: "list",
icon: "interface", icon: "interface",
rtl: 0,
title: "intf", title: "intf",
data: Interface, data: Interface,
}, },
{ {
type: "list", type: "list",
icon: "opts", icon: "opts",
rtl: 1,
title: "opts", title: "opts",
data: Options, data: Options,
}, },
{ {
type: "list", type: "list",
icon: "db", icon: "db",
rtl: 0,
title: "db", title: "db",
data: Database, data: Database,
}, },
{ {
type: "list", type: "list",
icon: "reset", icon: "reset",
rtl: 1,
title: "rest", title: "rest",
data: Reset, data: Reset,
}, },
{ {
type: "list", type: "list",
icon: "info", icon: "info",
rtl: 0,
title: "About", title: "About",
data: About, data: About,
}, },
@ -59,22 +64,16 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["icon"]), ...mapState(["icon", "RTL"]),
}, },
methods: { methods: {
...mapActions(["setComponent"]), pgLoad({ object }) {
onPageLoad({ object }) {
object.bindingContext = new Observable(); object.bindingContext = new Observable();
this.setComponent("Settings");
}, },
// HELPERS // HELPERS
navigateTo(view) { navigateTo(view) {
this.$navigateTo(view, { this.$navigateTo(view, {
transition: { animated: false,
name: "slide",
duration: 200,
curve: "easeOut",
},
}); });
}, },
}, },

View file

@ -1,16 +1,22 @@
<template> <template>
<Page @loaded="onPageLoad" actionBarHidden="true"> <Page @loaded="pgLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *"> <RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<OptionsList title="Settings" :items="items" /> <OptionsList title="Settings" :items="items" />
<GridLayout row="1" class="appbar" rows="*" columns="auto, *"> <GridLayout row="1" class="appbar rtl" rows="*" columns="auto, *">
<Button class="ico" :text="icon.back" @tap="$navigateBack()" /> <Button class="ico" :text="icon.back" @tap="$navigateBack()" />
</GridLayout> </GridLayout>
</GridLayout> </RGridLayout>
</Page> </Page>
</template> </template>
<script> <script>
import { Observable, Device, Application, Utils } from "@nativescript/core"; import {
Observable,
Device,
Application,
ApplicationSettings,
Utils,
} from "@nativescript/core";
import { mapState, mapActions } from "vuex"; import { mapState, mapActions } from "vuex";
import { localize } from "@nativescript/localize"; import { localize } from "@nativescript/localize";
import OptionsList from "../sub/OptionsList"; import OptionsList from "../sub/OptionsList";
@ -20,12 +26,13 @@ import * as utils from "~/shared/utils";
export default { export default {
components: { OptionsList }, components: { OptionsList },
computed: { computed: {
...mapState(["icon", "timerDelay", "timerSound", "timerVibrate"]), ...mapState(["icon", "timerDelay", "timerSound", "timerVibrate", "RTL"]),
items() { items() {
let options = [ let options = [
{ {
type: "list", type: "list",
icon: "sound", icon: "sound",
rtl: 0,
title: "tmrSnd", title: "tmrSnd",
subTitle: this.timerSound.title, subTitle: this.timerSound.title,
action: this.showSoundsList, action: this.showSoundsList,
@ -33,8 +40,9 @@ export default {
{ {
type: "switch", type: "switch",
icon: "vibrate", icon: "vibrate",
rtl: 0,
title: "tmrvbrt", title: "tmrvbrt",
checked: this.timerVibrate, checked: !!this.timerVibrate,
action: this.toggleTimerVibrate, action: this.toggleTimerVibrate,
}, },
]; ];
@ -42,6 +50,7 @@ export default {
{ {
type: "list", type: "list",
icon: "sound", icon: "sound",
rtl: 0,
title: "notifSetg", title: "notifSetg",
subTitle: null, subTitle: null,
action: this.openNotificationChannelSettings, action: this.openNotificationChannelSettings,
@ -53,39 +62,50 @@ export default {
{ {
type: "list", type: "list",
icon: "delay", icon: "delay",
rtl: 0,
title: "dlyDur", title: "dlyDur",
subTitle: this.timerDelay, subTitle:
this.delayList[
this.delayList.findIndex((e) => e.n == this.timerDelay)
].l,
action: this.showDelayList, action: this.showDelayList,
}, },
...list, ...list,
{}, {},
]; ];
}, },
}, delayList() {
methods: { return [
...mapActions([
"setTimerDelay",
"setTimerSound",
"setTimerVibrate",
"setComponent",
]),
onPageLoad({ object }) {
object.bindingContext = new Observable();
this.setComponent("CTSettings");
},
showDelayList() {
let list = [
...Array.from(Array(4), (_, x) => x + 1), ...Array.from(Array(4), (_, x) => x + 1),
...Array.from(Array(6), (_, x) => (x + 1) * 5), ...Array.from(Array(6), (_, x) => (x + 1) * 5),
].map( ].map((e) => {
(e, i) => `${e} ${i == 0 ? localize("minute") : localize("minutes")}` return {
); l: `${this.getLocaleN(e)} ${localize(e > 1 ? "minutes" : "minute")}`,
n: e,
};
});
},
},
methods: {
...mapActions(["setTimerDelay", "setTimerSound", "setTimerVibrate"]),
pgLoad({ object }) {
object.bindingContext = new Observable();
ApplicationSettings.setNumber("isTimer", 2);
},
showDelayList() {
this.$showModal(Action, { this.$showModal(Action, {
props: { props: {
title: "dlyDur", title: "dlyDur",
list, list: this.delayList.map((e) => e.l),
selected: this.delayList.findIndex((e) => e.n == this.timerDelay),
}, },
}).then((dur) => dur && this.setTimerDelay(dur)); }).then(
(res) =>
res &&
this.setTimerDelay(
this.delayList[this.delayList.findIndex((e) => e.l == res)].n
)
);
}, },
showSoundsList() { showSoundsList() {
let getTones = utils.getTones(); let getTones = utils.getTones();
@ -102,7 +122,7 @@ export default {
); );
}, },
toggleTimerVibrate() { toggleTimerVibrate() {
this.setTimerVibrate(!this.timerVibrate); this.setTimerVibrate(!this.timerVibrate | 0);
}, },
openNotificationChannelSettings() { openNotificationChannelSettings() {
const ctx = Application.android.context; const ctx = Application.android.context;

View file

@ -1,28 +1,30 @@
<template> <template>
<Page @loaded="onPageLoad" actionBarHidden="true"> <Page @loaded="pgLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *"> <RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<OptionsList title="db" :items="items" /> <OptionsList title="db" :items="items" />
<GridLayout <GridLayout
v-show="!toast && !progress" :hidden="toast || progress"
@loaded="abLoad"
row="1" row="1"
class="appbar" class="appbar rtl"
rows="*" rows="*"
columns="auto, *" columns="auto, *"
> >
<Button class="ico" :text="icon.back" @tap="$navigateBack()" /> <Button class="ico" :text="icon.back" @tap="$navigateBack()" />
</GridLayout> </GridLayout>
<Toast :toast="toast" :action="hideToast" /> <Toast :onload="tbLoad" :toast="toast" :action="hideBar" />
<GridLayout <RGridLayout
:rtl="RTL"
v-show="progress" v-show="progress"
row="1" row="1"
colSpan="2" colSpan="2"
class="appbar snackBar" class="appbar snackBar rtl"
columns="auto, *" columns="auto, *"
> >
<ActivityIndicator :busy="progress ? true : false" /> <ActivityIndicator :busy="!!progress" />
<Label col="1" class="title" :text="progress" textWrap="true" /> <RLabel col="1" class="title" :text="progress" />
</GridLayout> </RGridLayout>
</GridLayout> </RGridLayout>
</Page> </Page>
</template> </template>
@ -50,9 +52,11 @@ export default {
components: { OptionsList, Toast }, components: { OptionsList, Toast },
data() { data() {
return { return {
backupFolder: null, backupFolder: 0,
progress: null, progress: 0,
toast: null, toast: 0,
appbar: 0,
toastbar: 0,
}; };
}, },
computed: { computed: {
@ -65,6 +69,7 @@ export default {
"units", "units",
"mealPlans", "mealPlans",
"importSummary", "importSummary",
"RTL",
]), ]),
items() { items() {
return [ return [
@ -101,13 +106,15 @@ export default {
"importRecipesFromDB", "importRecipesFromDB",
"importMealPlansFromJSON", "importMealPlansFromJSON",
"importMealPlansFromDB", "importMealPlansFromDB",
"importTimerPresets",
"unlinkBrokenImages", "unlinkBrokenImages",
"clearImportSummary", "clearImportSummary",
]), ]),
onPageLoad({ object }) { pgLoad({ object }) {
object.bindingContext = new Observable(); object.bindingContext = new Observable();
const ContentResolver = Application.android.nativeApp.getContentResolver(); const ContentResolver =
this.backupFolder = ApplicationSettings.getString("backupFolder"); Application.android.nativeApp.getContentResolver();
this.backupFolder = ApplicationSettings.getString("backupFolder", null);
if ( if (
!this.backupFolder || !this.backupFolder ||
ContentResolver.getPersistedUriPermissions().isEmpty() ContentResolver.getPersistedUriPermissions().isEmpty()
@ -115,22 +122,33 @@ export default {
this.backupFolder = null; this.backupFolder = null;
} }
}, },
// BACKUP FOLDER PICKER abLoad({ object }) {
this.appbar = object;
},
tbLoad({ object }) {
this.toastbar = object;
},
// BackupFolderPicker
setBackupFolder(startExport) { setBackupFolder(startExport) {
const ContentResolver = Application.android.nativeApp.getContentResolver(); const ContentResolver =
Application.android.nativeApp.getContentResolver();
const FLAGS = const FLAGS =
android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION | android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION |
android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION; android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
utils.getBackupFolder().then((uri) => { utils.getBackupFolder().then((uri) => {
console.log(uri.toString());
if (uri != null) { if (uri != null) {
if (this.backupFolder) // ReleaseExistingPermissions
if (this.backupFolder && this.backupFolder != uri.toString())
ContentResolver.releasePersistableUriPermission( ContentResolver.releasePersistableUriPermission(
new android.net.Uri.parse(this.backupFolder), new android.net.Uri.parse(this.backupFolder),
FLAGS FLAGS
); );
this.backupFolder = uri.toString(); this.backupFolder = uri.toString();
ApplicationSettings.setString("backupFolder", this.backupFolder); ApplicationSettings.setString("backupFolder", this.backupFolder);
// PERSIST PERMISSIONS // PersistPermissions
ContentResolver.takePersistableUriPermission(uri, FLAGS); ContentResolver.takePersistableUriPermission(uri, FLAGS);
if (startExport && this.backupFolder) { if (startExport && this.backupFolder) {
this.exportBackup(); this.exportBackup();
@ -141,18 +159,15 @@ export default {
// EXPORT HANDLERS // EXPORT HANDLERS
exportCheck() { exportCheck() {
const ContentResolver = Application.android.nativeApp.getContentResolver(); const ContentResolver =
if (!this.recipes.length) { Application.android.nativeApp.getContentResolver();
this.toast = localize("aFBu"); if (!this.recipes.length) this.showToast(localize("aFBu"));
utils.timer(5, (val) => { else {
if (!val) this.toast = val;
});
} else {
if ( if (
!this.backupFolder || !this.backupFolder ||
ContentResolver.getPersistedUriPermissions().isEmpty() ContentResolver.getPersistedUriPermissions().isEmpty()
) { ) {
this.setBackupFolder(true); this.setBackupFolder(1);
} else this.exportBackup(); } else this.exportBackup();
} }
}, },
@ -182,7 +197,7 @@ export default {
console.log("Backup error: ", err); console.log("Backup error: ", err);
this.progress = null; this.progress = null;
this.releaseBackEvent(); this.releaseBackEvent();
this.setBackupFolder(true); this.setBackupFolder(1);
}); });
}, },
showExportSummary(filename) { showExportSummary(filename) {
@ -248,6 +263,8 @@ export default {
importImages(); importImages();
} else if (File.exists(recipes)) { } else if (File.exists(recipes)) {
// IMPORT FROM JSON FILES // IMPORT FROM JSON FILES
console.log("import from json");
this.isFileDataValid([ this.isFileDataValid([
{ {
path: recipes, path: recipes,
@ -285,9 +302,10 @@ export default {
} else this.failedImport(localize("buInc")); } else this.failedImport(localize("buInc"));
}, },
isFileDataValid(file) { isFileDataValid(file) {
console.log("isFileDataValid");
const files = file.filter((e) => File.exists(e.path)); const files = file.filter((e) => File.exists(e.path));
if (files.length) { if (files.length) {
let isValid = files.map(() => false); let isValid = files.map(() => 0);
files.forEach((file, i) => { files.forEach((file, i) => {
File.fromPath(file.path) File.fromPath(file.path)
.readText() .readText()
@ -299,7 +317,7 @@ export default {
); );
return 0; return 0;
} }
if (isValid.every((e) => e === true)) { if (isValid.every((e) => e === 1)) {
files.forEach((file) => { files.forEach((file) => {
File.fromPath(file.path) File.fromPath(file.path)
.readText() .readText()
@ -328,9 +346,9 @@ export default {
try { try {
JSON.parse(data) && Array.isArray(JSON.parse(data)); JSON.parse(data) && Array.isArray(JSON.parse(data));
} catch (e) { } catch (e) {
return false; return 0;
} }
return true; return 1;
}, },
extractData(recipesDB) { extractData(recipesDB) {
const db = openOrCreate(recipesDB); const db = openOrCreate(recipesDB);
@ -356,8 +374,14 @@ export default {
db.select(`SELECT * FROM mealPlans`).then((res) => db.select(`SELECT * FROM mealPlans`).then((res) =>
this.importMealPlansFromDB(res) this.importMealPlansFromDB(res)
); );
// Import timerPresets
db.select(`SELECT * FROM timerPresets`).then((res) =>
this.importTimerPresets(res)
);
}, },
importData(data, db) { importData(data, db) {
console.log("importing");
switch (db) { switch (db) {
case "recipes": case "recipes":
this.importRecipesFromJSON(data); this.importRecipesFromJSON(data);
@ -405,7 +429,6 @@ export default {
File.fromPath(entity._path).remove(); File.fromPath(entity._path).remove();
}); });
}); });
this.showImportSummary(); this.showImportSummary();
this.unlinkBrokenImages(); this.unlinkBrokenImages();
} }
@ -447,9 +470,19 @@ export default {
args.cancel = true; args.cancel = true;
}, },
// HELPERS // TOAST
hideToast() { showToast(data) {
this.animateBar(this.appbar, 0).then(() => {
this.toast = data;
this.animateBar(this.toastbar, 1);
utils.timer(5, (val) => !val && this.hideBar());
});
},
hideBar() {
this.animateBar(this.toastbar, 0).then(() => {
this.toast = null; this.toast = null;
this.animateBar(this.appbar, 1);
});
}, },
}, },
}; };

View file

@ -1,14 +1,8 @@
<template> <template>
<Page @loaded="pgLoad" actionBarHidden="true"> <Page @loaded="pgLoad" actionBarHidden="true">
<RGridLayout :isRtl="RTL" rows="*, auto" columns="auto, *"> <RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<OptionsList title="intf" :items="items" /> <OptionsList title="intf" :items="items" />
<GridLayout <GridLayout row="1" class="appbar rtl" rows="*" columns="auto, *">
:isRtl="RTL"
row="1"
class="appbar"
rows="*"
columns="auto, *"
>
<Button class="ico" :text="icon.back" @tap="$navigateBack()" /> <Button class="ico" :text="icon.back" @tap="$navigateBack()" />
</GridLayout> </GridLayout>
</RGridLayout> </RGridLayout>
@ -16,18 +10,12 @@
</template> </template>
<script> <script>
import { import { ApplicationSettings, Observable, Frame } from "@nativescript/core";
ApplicationSettings,
Observable,
Device,
Frame,
} from "@nativescript/core";
import { localize } from "@nativescript/localize";
import Action from "../modals/Action"; import Action from "../modals/Action";
import Confirm from "../modals/Confirm";
import OptionsList from "../sub/OptionsList"; import OptionsList from "../sub/OptionsList";
import { mapState, mapActions } from "vuex"; import { mapState, mapActions } from "vuex";
import * as utils from "~/shared/utils"; import * as utils from "~/shared/utils";
import { localize } from "@nativescript/localize";
export default { export default {
components: { OptionsList }, components: { OptionsList },
@ -37,29 +25,34 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["icon", "language", "appTheme", "layout", "RTL"]), ...mapState(["icon", "language", "theme", "layout", "RTL"]),
items() { items() {
return [ return [
{}, {},
{ {
type: "list", type: "list",
icon: "lang", icon: "lang",
rtl: 0,
title: "lang", title: "lang",
subTitle: this.applang, subTitle: localize(this.applang),
action: this.setAppLang, action: this.setAppLang,
}, },
{ {
type: "list", type: "list",
icon: "theme", icon: "theme",
rtl: 0,
title: "Theme", title: "Theme",
subTitle: ApplicationSettings.getString("appTheme", "sysDef"), subTitle: localize(
ApplicationSettings.getString("theme", "sysDef")
),
action: this.selectThemes, action: this.selectThemes,
}, },
{ {
type: "list", type: "list",
icon: "layout", icon: "layout",
rtl: 1,
title: "listVM", title: "listVM",
subTitle: this.layout, subTitle: localize(this.layout),
action: this.setLayoutMode, action: this.setLayoutMode,
}, },
{}, {},
@ -93,6 +86,7 @@ export default {
ApplicationSettings.setString("appLocale", locale); ApplicationSettings.setString("appLocale", locale);
utils.updateLocale(); utils.updateLocale();
this.setRTL(); this.setRTL();
Frame.reloadPage();
} }
} }
}); });
@ -107,9 +101,9 @@ export default {
}).then((action) => { }).then((action) => {
if ( if (
action && action &&
(ApplicationSettings.getString("appTheme") != this.appTheme (ApplicationSettings.getString("theme") != this.theme
? 1 ? 1
: this.appTheme != action) : this.theme != action)
) { ) {
this.setTheme(action); this.setTheme(action);
Frame.reloadPage(); Frame.reloadPage();

View file

@ -1,44 +1,80 @@
<template> <template>
<Page @loaded="onPageLoad" actionBarHidden="true"> <Page @loaded="pgLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *"> <RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<OptionsList title="Settings" :items="items" /> <OptionsList title="Settings" :items="items" />
<GridLayout row="1" class="appbar" rows="*" columns="auto, *"> <GridLayout row="1" class="appbar rtl" rows="*" columns="auto, *">
<Button class="ico" :text="icon.back" @tap="$navigateBack()" /> <Button class="ico" :text="icon.back" @tap="$navigateBack()" />
</GridLayout> </GridLayout>
</GridLayout> </RGridLayout>
</Page> </Page>
</template> </template>
<script> <script>
import { Observable } from "@nativescript/core"; import { Observable } from "@nativescript/core";
import { mapState, mapActions } from "vuex"; import { mapState, mapActions } from "vuex";
import Action from "../modals/Action";
import OptionsList from "../sub/OptionsList"; import OptionsList from "../sub/OptionsList";
import { localize } from "@nativescript/localize";
export default { export default {
components: { OptionsList }, components: { OptionsList },
computed: { computed: {
...mapState(["icon", "mondayFirst"]), ...mapState(["icon", "mondayFirst", "RTL", "plannerView", "planDeletion"]),
items() { items() {
return [ return [
{}, {},
{
type: "list",
icon: "calv",
title: "calVM",
subTitle: localize(this.plannerView),
action: this.selectPlannerView,
},
{ {
type: "switch", type: "switch",
icon: "week", icon: "week",
title: "swm", title: "swm",
checked: this.mondayFirst, checked: !!this.mondayFirst,
action: this.toggleFirstDay, action: this.toggleFirstDay,
}, },
{
type: "list",
icon: "mpd",
title: "admp",
subTitle: localize(this.planDeletion),
action: this.selectDeletionTime,
},
{}, {},
]; ];
}, },
}, },
methods: { methods: {
...mapActions(["setFirstDay"]), ...mapActions(["setFirstDay", "setPlannerView", "setPlanDeletion"]),
onPageLoad({ object }) { pgLoad({ object }) {
object.bindingContext = new Observable(); object.bindingContext = new Observable();
}, },
toggleFirstDay() { toggleFirstDay() {
this.setFirstDay(!this.mondayFirst); this.setFirstDay(!this.mondayFirst | 0);
},
selectPlannerView() {
this.$showModal(Action, {
props: {
title: "calVM",
list: ["mnth", "wk", "d"],
},
}).then((res) => {
if (res && this.plannerView != res) this.setPlannerView(res);
});
},
selectDeletionTime() {
this.$showModal(Action, {
props: {
title: "admp",
list: ["otay", "otam", "otaw", "nvr"],
},
}).then((res) => {
if (res && this.planDeletion != res) this.setPlanDeletion(res);
});
}, },
}, },
}; };

View file

@ -1,18 +1,19 @@
<template> <template>
<Page @loaded="onPageLoad" actionBarHidden="true"> <Page @loaded="pgLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *"> <RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<OptionsList title="opts" :items="items" /> <OptionsList title="opts" :items="items" />
<GridLayout <GridLayout
v-show="!toast" :hidden="toast"
@loaded="abLoad"
row="1" row="1"
class="appbar" class="appbar rtl"
rows="*" rows="*"
columns="auto, *" columns="auto, *"
> >
<Button class="ico" :text="icon.back" @tap="$navigateBack()" /> <Button class="ico" :text="icon.back" @tap="$navigateBack()" />
</GridLayout> </GridLayout>
<Toast :toast="toast" :action="(toast = null)" /> <Toast :onload="tbLoad" :toast="toast" :action="hideBar" />
</GridLayout> </RGridLayout>
</Page> </Page>
</template> </template>
@ -28,11 +29,13 @@ export default {
components: { OptionsList, Toast }, components: { OptionsList, Toast },
data() { data() {
return { return {
appbar: null,
toastbar: null,
toast: null, toast: null,
}; };
}, },
computed: { computed: {
...mapState(["icon", "shakeEnabled"]), ...mapState(["icon", "shake", "RTL"]),
items() { items() {
return [ return [
{}, {},
@ -40,8 +43,8 @@ export default {
type: "switch", type: "switch",
icon: "shuf", icon: "shuf",
title: "sVw", title: "sVw",
subTitle: "sVwInfo", subTitle: localize("sVwInfo"),
checked: this.shakeEnabled, checked: !!this.shake,
action: this.toggleShake, action: this.toggleShake,
}, },
{}, {},
@ -50,19 +53,35 @@ export default {
}, },
methods: { methods: {
...mapActions(["setShake"]), ...mapActions(["setShake"]),
onPageLoad({ object }) { pgLoad({ object }) {
object.bindingContext = new Observable(); object.bindingContext = new Observable();
}, },
abLoad({ object }) {
this.appbar = object;
},
tbLoad({ object }) {
this.toastbar = object;
},
// SHAKE VIEW RANDOM RECIPE // SHAKE VIEW RANDOM RECIPE
toggleShake() { toggleShake() {
let checked = this.shakeEnabled; let checked = this.shake;
if (checked && !utils.hasAccelerometer()) { if (checked && !utils.hasAccelerometer())
this.toast = localize("noAccSensor"); this.showToast(localize("noAccSensor"));
utils.timer(5, (val) => { else this.setShake(!checked | 0);
if (!val) this.toast = val; },
showToast(data) {
this.animateBar(this.appbar, 0).then(() => {
this.toast = data;
this.animateBar(this.toastbar, 1);
utils.timer(5, (val) => !val && this.hideBar());
});
},
hideBar() {
this.animateBar(this.toastbar, 0).then(() => {
this.toast = null;
this.animateBar(this.appbar, 1);
}); });
} else this.setShake(!checked);
}, },
}, },
}; };

View file

@ -1,108 +1,107 @@
<template> <template>
<Page @loaded="onPageLoad" actionBarHidden="true"> <Page @loaded="pgLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *"> <RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<OptionsList title="rest" :items="items" :action="resetListItems" /> <OptionsList title="rest" :items="items" :action="resetListItems" />
<GridLayout <GridLayout
:hidden="toast" :hidden="toast"
row="1" row="1"
class="appbar" class="appbar rtl"
@loaded="onAppBarLoad" @loaded="abLoad"
columns="auto, *" columns="auto, *"
> >
<Button class="ico" :text="icon.back" @tap="$navigateBack()" /> <Button class="ico" :text="icon.back" @tap="$navigateBack()" />
</GridLayout> </GridLayout>
<Toast :toast="toast" :action="hideToast" /> <Toast :onload="tbLoad" :toast="toast" :action="hideToast" />
</GridLayout> </RGridLayout>
</Page> </Page>
</template> </template>
<script> <script>
import { Observable, CoreTypes } from "@nativescript/core"; import { Observable } from "@nativescript/core";
import { localize } from "@nativescript/localize"; import { localize } from "@nativescript/localize";
import { mapState, mapActions } from "vuex"; import { mapState, mapActions } from "vuex";
import * as utils from "~/shared/utils"; import * as utils from "~/shared/utils";
import OptionsList from "../sub/OptionsList"; import OptionsList from "../sub/OptionsList";
import Toast from "../sub/Toast"; import Toast from "../sub/Toast";
let barTimer;
export default { export default {
components: { OptionsList, Toast }, components: { OptionsList, Toast },
data() { data() {
return { return {
toast: null, toast: 0,
appbar: null, appbar: 0,
toastbar: 0,
}; };
}, },
computed: { computed: {
...mapState(["icon"]), ...mapState(["icon", "RTL"]),
items() { items() {
return [ return [
{}, {},
{ {
type: "list", type: "list",
icon: "reset", icon: "reset",
rtl: 1,
title: "restCuiL", title: "restCuiL",
data: "cuisines", data: "cuisines",
}, },
{ {
type: "list", type: "list",
icon: "reset", icon: "reset",
rtl: 1,
title: "restCatL", title: "restCatL",
data: "categories", data: "categories",
}, },
{ {
type: "list", type: "list",
icon: "reset", icon: "reset",
rtl: 1,
title: "restYUL", title: "restYUL",
data: "yieldUnits", data: "yieldUnits",
}, },
{ {
type: "list", type: "list",
icon: "reset", icon: "reset",
rtl: 1,
title: "restUL", title: "restUL",
data: "units", data: "units",
}, },
{}, {
type: "info",
title: "restInfo",
},
{}, {},
]; ];
}, },
}, },
methods: { methods: {
...mapActions(["resetListItemsAction"]), ...mapActions(["resetListItemsAction"]),
onPageLoad({ object }) { pgLoad({ object }) {
object.bindingContext = new Observable(); object.bindingContext = new Observable();
}, },
onAppBarLoad({ object }) { abLoad({ object }) {
this.appbar = object; this.appbar = object;
}, },
tbLoad({ object }) {
this.toastbar = object;
},
// RESET // RESET
resetListItems(listName) { resetListItems(listName) {
this.resetListItemsAction(listName); this.resetListItemsAction(listName);
this.showToast(); this.showToast();
}, },
showToast() { showToast() {
this.animateBar(this.appbar, 0).then(() => {
this.toast = localize("restDone"); this.toast = localize("restDone");
utils.timer(5, (val) => { this.animateBar(this.toastbar, 1);
if (!val) this.toast = val;
}); });
utils.timer(5, (val) => !val && this.hideToast());
}, },
hideToast({ object }) { hideToast() {
this.appbar.translateY = 64; this.animateBar(this.toastbar, 0).then(() => {
object
.animate({
opacity: 0,
translate: { x: 0, y: 64 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
})
.then(() => {
this.showUndo = false;
this.toast = null; this.toast = null;
this.appbar.animate({ this.animateBar(this.appbar, 1);
translate: { x: 0, y: 0 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
});
object.opacity = 1;
object.translateY = 0;
}); });
}, },
}, },

View file

@ -1,42 +1,57 @@
<template> <template>
<ListView colSpan="2" rowSpan="2" class="options-list" for="item in items"> <ListView colSpan="2" rowSpan="2" class="options" for="item in items">
<v-template if="$index == 0"> <v-template if="$index == 0">
<Label class="pageTitle" :text="title | L" /> <Label class="pageTitle" :text="title | L" />
</v-template> </v-template>
<v-template if="item.type == 'switch'"> <v-template if="item.type == 'switch'">
<GridLayout <RGridLayout
:rtl="RTL"
columns="auto, *, auto" columns="auto, *, auto"
class="option" class="option"
@touch="touch($event, item.data, item.action)" @touch="touch($event, item.data, item.action)"
> >
<Label class="ico" :text="icon[item.icon]" /> <Label class="ico rtl" :text="icon[item.icon]" />
<StackLayout col="1" verticalAlignment="center"> <StackLayout col="1" class="info">
<Label :text="item.title | L" class="info" /> <RLabel :text="item.title | L" class="tw" />
<Label v-if="item.subTitle" :text="item.subTitle | L" class="sub" /> <RLabel
v-if="item.subTitle"
:text="item.subTitle | L"
class="sub tw"
/>
</StackLayout> </StackLayout>
<Switch <Switch
@loaded="swLoad"
isUserInteractionEnabled="false" isUserInteractionEnabled="false"
:color="item.checked ? '#ff5200' : '#adb5bd'" :color="item.checked ? '#ff5200' : '#adb5bd'"
col="2" col="2"
:checked="item.checked" :checked="item.checked"
/> />
</GridLayout> </RGridLayout>
</v-template> </v-template>
<v-template if="item.type == 'list'"> <v-template if="item.type == 'list'">
<GridLayout <RGridLayout
:rtl="RTL"
columns="auto, *" columns="auto, *"
class="option" class="option"
@touch="touch($event, item.data, item.action)" @touch="touch($event, item.data, item.action)"
> >
<Label class="ico" :text="icon[item.icon]" /> <Label class="ico" :class="{ rtl: item.rtl }" :text="icon[item.icon]" />
<StackLayout col="1"> <StackLayout col="1" class="info">
<Label :text="item.title | L" class="info" /> <RLabel :text="item.title | L" class="tw" />
<Label v-if="item.subTitle" :text="item.subTitle" class="sub" /> <RLabel
:hidden="!item.subTitle"
:text="item.subTitle"
class="sub tw"
/>
</StackLayout> </StackLayout>
</GridLayout> </RGridLayout>
</v-template> </v-template>
<v-template if="item.type == 'info'"> <v-template if="item.type == 'info'">
<Label class="group-info sub tw" :text="item.title | L" /> <Label
class="group-info sub tw"
:class="{ r: RTL }"
:text="item.title | L"
/>
</v-template> </v-template>
<v-template> <v-template>
<StackLayout class="listSpace"> </StackLayout> <StackLayout class="listSpace"> </StackLayout>
@ -46,13 +61,19 @@
<script> <script>
import { mapState } from "vuex"; import { mapState } from "vuex";
import * as utils from "~/shared/utils";
export default { export default {
props: ["title", "items", "action"], props: ["title", "items", "action"],
computed: { computed: {
...mapState(["icon"]), ...mapState(["icon", "RTL"]),
}, },
methods: { methods: {
swLoad({ object }) {
object.android.setRotation(
this.RTL && utils.sysRTL() ? 0 : this.RTL || utils.sysRTL() ? 180 : 0
);
},
touch({ object, action }, data, localAction) { touch({ object, action }, data, localAction) {
object.className = action.match(/down|move/) ? "option fade" : "option"; object.className = action.match(/down|move/) ? "option fade" : "option";
if (action == "up") localAction ? localAction(data) : this.action(data); if (action == "up") localAction ? localAction(data) : this.action(data);

View file

@ -1,23 +1,25 @@
<template> <template>
<GridLayout <RGridLayout
:rtl="RTL"
row="1" row="1"
class="appbar snackBar" class="appbar snackBar"
columns="auto, *, auto" columns="auto, *, auto"
@swipe="action" @swipe="action"
@loaded="onload"
> >
<Button :text="count" class="ico countdown tb" /> <Button @tap="action" :text="count" class="ico countdown tb" />
<Label class="title" col="1" :text="msg | L" /> <RLabel @tap="action" class="title" col="1" :text="msg | L" />
<Button class="ico fab" :text="icon.undo" @tap="undo" col="3" /> <Button class="ico fab rtl" :text="icon.undo" @tap="undo" col="3" />
</GridLayout> </RGridLayout>
</template> </template>
<script> <script>
import { mapState } from "vuex"; import { mapState } from "vuex";
export default { export default {
props: ["count", "msg", "undo", "action"], props: ["count", "msg", "undo", "action", "onload"],
computed: { computed: {
...mapState(["icon"]), ...mapState(["icon", "RTL"]),
}, },
}; };
</script> </script>

View file

@ -1,193 +1,280 @@
<template> <template>
<GridLayout <RGridLayout
:rtl="RTL"
rows="auto, auto" rows="auto, auto"
columns="auto, *, auto, auto, auto" columns="auto, *, auto, auto, auto"
class="singleTimer" class="timer"
> >
<!-- :class="{ blink: done }" -->
<Button <Button
class="ico" class="ico min rtl"
:text="done ? icon.ring : timer.isPaused ? icon.start : icon.pause" :text="done ? icon.ring : timer.isPaused ? icon.start : icon.pause"
@tap="!done && toggleProgress()" @tap="!done && toggleProgress()"
/> />
<StackLayout col="1" class="info" :colSpan="timer.isPaused ? 1 : 2"> <StackLayout
<Label :text="timer.label" class="tb title tw" /> col="1"
<Label class="info"
@touch="!timer.recipeID && touch($event)" :colSpan="timer.isPaused || !timer.preset ? 1 : 2"
>
<RLabel :text="timer.label" class="tb title tw a" />
<RLabel
:hidden="!timer.recipeID && done"
@touch="!done && touch($event)"
:text="getRecipeTitle" :text="getRecipeTitle"
class="recipeTitle" class="a"
:class="timer.recipeID ? 'sub' : 'clickable'" :class="timer.recipeID ? 'sub' : 'accent'"
/>
<RLabel
:text="
progress == 0
? countUp
? getCount
: formattedTime(timer.time)
: getCount
"
/> />
<Label :text="formattedTime(timer.time)" />
</StackLayout> </StackLayout>
<Button <Button
col="2" col="2"
class="ico" class="ico rtl"
:hidden="(!timer.isPaused || progress == 0) && !done" :hidden="(!timer.isPaused && timer.preset) || done || countUp"
:text="icon.reset" :text="isReset || timer.preset ? icon.reset : icon.addTo"
@tap="resetTimer" @tap="isReset || timer.preset ? resetTimer() : addPreset()"
/> />
<Button <Button
col="3" col="3"
class="ico" class="ico"
:hidden="timer.preset && !done" @tap="countUp ? addPreset() : addDelay()"
@tap="done ? delay() : addPreset()" :text="countUp ? icon.addTo : icon.delay"
:text="done ? icon.delay : icon.addTo"
/> />
<Button <Button
col="4" col="4"
class="ico x" class="ico min"
:text="icon.x" :text="icon.x"
@tap="removeTimerItem(timerIndex, false)" @tap="removeTimer(timer.id, done)"
/> />
<Progress row="1" colSpan="5" :value="progress" /> <Progress @loaded="pLoaded" row="1" colSpan="5" />
</GridLayout> </RGridLayout>
</template> </template>
<script> <script>
import { ApplicationSettings, Application } from "@nativescript/core"; import { ApplicationSettings } from "@nativescript/core";
import { localize } from "@nativescript/localize"; import { localize } from "@nativescript/localize";
import { mapState, mapActions } from "vuex"; import { mapState, mapActions } from "vuex";
import ActionWithSearch from "../modals/ActionWithSearch"; import ActionWithSearch from "../modals/ActionWithSearch";
import ViewRecipe from "../ViewRecipe";
import * as utils from "~/shared/utils"; import * as utils from "~/shared/utils";
import { EventBus } from "~/main"; import { EvtBus } from "~/main";
export default { export default {
props: [ props: [
"timer", "timer",
"timerIndex",
"formattedTime", "formattedTime",
"removeTimer", "removeTimer",
"addToPreset",
"togglePause", "togglePause",
"fireTimer", "timerAlert",
"showToast",
], ],
data() { data() {
return { return {
pBar: 0,
count: 0,
delay: 0,
progress: 0, progress: 0,
timerInt: this.timer.timerInterval,
}; };
}, },
computed: { computed: {
...mapState(["icon", "recipes", "timerDelay"]), ...mapState(["icon", "recipes", "timerDelay", "timerPresets", "RTL"]),
getRecipeTitle() { getRecipeTitle() {
let { recipeID } = this.timer; let { recipeID } = this.timer;
if (recipeID) { if (recipeID) {
let recipe = this.recipes.filter( let recipe = this.recipes.filter((e) => e.id == this.timer.recipeID)[0];
(e) => e.id === this.timer.recipeID if (recipe) return recipe.title;
)[0]; else {
return recipe this.timer.recipeID = null;
? recipe.title return localize("fwr");
: `[ ${this.$options.filters.L("resNF")} ]`; }
} else return localize("fwr"); } else return localize("fwr");
}, },
done() { done() {
return this.progress >= 100; return this.timer.done;
}, },
getTimeInSec() { countUp() {
return this.timer.mode == 0;
},
isReset() {
return this.timer.isPaused && this.progress != 0;
},
getTotalTime() {
return this.delay + this.actualTime;
},
actualTime() {
let t = this.timer.time.split(":"); let t = this.timer.time.split(":");
return +t[0] * 60 * 60 + +t[1] * 60 + +t[2]; return +t[0] * 60 * 60 + +t[1] * 60 + +t[2];
}, },
getCount() {
let c = this.count;
let s = Math.abs(c);
return (
(c < 0 ? "-" : "") +
new Date(s * 1000)
.toISOString()
.slice(
s < 10
? 18
: s < 60
? 17
: s < 600
? 15
: s < 3600
? 14
: s < 36000
? 12
: s < 86400
? 11
: 0,
19
)
);
},
}, },
methods: { methods: {
...mapActions(["removeActiveTimer", "updateActiveTimer", "addTimerPreset"]), ...mapActions([
"removeActiveTimer",
"addTimerPreset",
"deleteTimerPreset",
"sortActiveTimers",
]),
pLoaded({ object }) {
this.pBar = object.android;
this.pBar.setRotation(
this.RTL && utils.sysRTL() ? 0 : this.RTL || utils.sysRTL() ? 180 : 0
);
this.initTimer();
},
viewRecipe(recipeID) {
this.$navigateTo(ViewRecipe, {
props: {
recipeID,
},
});
},
attachRecipe() { attachRecipe() {
this.$showModal(ActionWithSearch, { this.$showModal(ActionWithSearch, {
props: { props: {
title: "selRec", title: "selRec",
recipes: this.recipes, recipes: this.recipes,
action: "aNBtn",
}, },
}).then((recipeID) => { }).then((res) => {
if (res == "aNBtn") {
this.$navigateTo(EditRecipe, {
animated: false,
});
} else if (res) {
let timer = this.timer; let timer = this.timer;
timer.recipeID = recipeID; timer.recipeID = res;
this.updateActiveTimer(timer); this.sortActiveTimers();
}
}); });
}, },
setProgress(progress, delay) { setNum(type, val) {
this.progress = progress; ApplicationSettings.setNumber(`${this.timer.id}${type}`, val);
let timer = this.timer; },
if (progress <= 100 && !timer.timerInterval) { setProgress() {
timer.timerInterval = setInterval(() => { this.progress = 100 - (this.count / this.getTotalTime) * 100;
this.pBar.setProgress(this.progress, true);
},
initTimer() {
this.resetInterval();
this.setProgress();
!this.timer.isPaused && this.pBar.setIndeterminate(this.countUp);
if (this.progress < 100 || !this.timer.timerInt) {
this.timer.timerInt = setInterval(() => {
if (!this.timer.isPaused) { if (!this.timer.isPaused) {
this.progress += 100 / (delay + this.getTimeInSec); this.setNum("c", this.countUp ? this.count++ : --this.count);
ApplicationSettings.setNumber( this.setProgress();
`${this.timer.id}progress`, } else this.resetInterval();
this.progress if (this.progress >= 100 && this.count >= 0) {
); this.timer.done = 1;
} this.timerAlert();
if (this.progress >= 100) {
if (progress < 100) this.fireTimer(timer);
clearInterval(timer.timerInterval);
ApplicationSettings.remove(`${this.timer.id}delay`);
} }
}, 1000); }, 1000);
this.updateActiveTimer(timer);
} }
}, },
resetInterval() { resetInterval() {
let timer = this.timer; clearInterval(this.timer.timerInt);
clearInterval(this.timer.timerInterval); this.timer.timerInt = 0;
timer.timerInterval = null; this.pBar.setIndeterminate(false);
this.updateActiveTimer(timer);
}, },
resetTimer() { resetTimer() {
this.resetInterval(); this.count = this.actualTime;
this.togglePause(this.timer, true); this.progress = this.delay = this.timer.done = 0;
ApplicationSettings.remove(`${this.timer.id}delay`); ApplicationSettings.remove(`${this.timer.id}d`);
this.setProgress(0, 0); this.setNum("c", this.count);
ApplicationSettings.setNumber(`${this.timer.id}progress`, 0); this.toggleProgress(1);
this.clearNotification(); this.pBar.setProgress(0, true);
}, },
clearNotification() { toggleProgress(n) {
Application.android.unregisterBroadcastReceiver("timer" + this.timer.id); this.togglePause(this.timer, n);
utils.TimerNotif.clear(this.timer.id); this.timer.isPaused ? this.resetInterval() : this.initTimer();
},
toggleProgress(bool) {
this.togglePause(this.timer, bool);
this.timer.isPaused
? this.resetInterval()
: this.setProgress(
ApplicationSettings.getNumber(`${this.timer.id}progress`, 0),
ApplicationSettings.getNumber(`${this.timer.id}delay`, 0)
);
},
removeTimerItem(index, noUndo) {
this.resetInterval();
this.removeTimer(this.timer.id, index, noUndo);
}, },
addPreset() { addPreset() {
let exist = this.timerPresets.some((e) => e.id == this.timer.id);
this.timer.preset = 1; this.timer.preset = 1;
this.addToPreset(this.timer); if (this.countUp) {
this.timer.time = new Date(this.count * 1000)
.toISOString()
.substr(11, 8);
}
let timer = JSON.parse(JSON.stringify(this.timer));
let { recipeID, timerInt, isPaused, preset, done, mode, ...presetTimer } =
timer;
this.addTimerPreset(presetTimer);
exist ? this.showToast("prstTU") : this.showToast("aTPrst");
}, },
delay() { addDelay() {
let delayInS = this.timerDelay.split(" ")[0] * 60; this.timer.done = 0;
ApplicationSettings.setNumber(`${this.timer.id}delay`, delayInS); let td = this.timerDelay;
let progress = (100 / delayInS) * this.getTimeInSec; let delayDur =
this.getLocaleN(td) + " " + localize(td > 1 ? "minutes" : "minute");
this.showToast(localize("wDBy", this.timer.label, delayDur));
let delay = td * 60;
if (this.done) this.delay = delay;
else this.delay += delay;
if (this.count >= 0) this.count += delay;
else this.count = this.delay;
this.setNum("d", this.delay);
this.setNum("c", this.count);
this.resetInterval(); this.resetInterval();
this.setProgress(progress, delayInS); this.initTimer();
this.clearNotification(); this.timerAlert();
}, },
// HELPERS // HELPERS
touch({ object, action }) { touch({ object, action }) {
object.className = action.match(/down|move/) let classes = object.className;
? "recipeTitle clickable fade" classes = action.match(/down|move/)
: "recipeTitle clickable"; ? !classes.includes("fade")
? classes + " fade"
: classes
: classes.replace(/ fade/g, "");
if (action == "up") this.attachRecipe(); if (action == "up") this.attachRecipe();
}, },
}, },
mounted() { created() {
this.setProgress( this.delay = ApplicationSettings.getNumber(`${this.timer.id}d`, 0);
ApplicationSettings.getNumber(`${this.timer.id}progress`, 0), this.count = ApplicationSettings.getNumber(
ApplicationSettings.getNumber(`${this.timer.id}delay`, 0) `${this.timer.id}c`,
this.actualTime
); );
EventBus.$on("timer" + this.timer.id, (e) => { let bID = "timer" + this.timer.id;
EvtBus.$off(bID);
EvtBus.$on(bID, (e) => {
switch (e) { switch (e) {
case "stop":
this.resetTimer();
break;
case "delay": case "delay":
this.delay(); this.addDelay();
break;
case "dismiss":
this.removeTimer(this.timer.id, 1);
break; break;
} }
}); });

View file

@ -1,20 +1,22 @@
<template> <template>
<GridLayout <GridLayout
v-show="toast" :hidden="!toast"
row="1" row="1"
colSpan="2" colSpan="2"
class="appbar snackBar" class="appbar snackBar"
columns="*" columns="*"
@swipe="action" @swipe="action"
@tap="action"
@loaded="onload"
> >
<FlexboxLayout minHeight="48" alignItems="center"> <StackLayout minHeight="48">
<Label class="title msg" :text="toast" /> <RLabel class="title msg" :text="toast" />
</FlexboxLayout> </StackLayout>
</GridLayout> </GridLayout>
</template> </template>
<script> <script>
export default { export default {
props: ["toast", "action"], props: ["toast", "action", "onload"],
}; };
</script> </script>

Binary file not shown.

View file

@ -290,13 +290,13 @@
"yld": "Ergebnis", "yld": "Ergebnis",
"buto": "Gesichert in %s", "buto": "Gesichert in %s",
"sysDefB": "Systemstandard + schwarz", "sysDefB": "Systemstandard + schwarz",
"tmr": "Eieruhr %s", "tmr": "Kurzzeitwecker %s",
"strtBtn": "START", "strtBtn": "START",
"ntmr": "Neue Eieruhr", "ntmr": "Neuer Kurzzeitwecker",
"timer": "Eieruhr", "timer": "Kurzzeitwecker",
"sec": "s", "sec": "s",
"tmrvbrt": "Eieruhrvibration", "tmrvbrt": "Kurzzeitweckervibration",
"tmrSnd": "Eieruhrton", "tmrSnd": "Kurzzeitweckerton",
"aTPrst": "Zu Voreinstellungen hinzugefügt", "aTPrst": "Zu Voreinstellungen hinzugefügt",
"fwr": "für welches Rezept?", "fwr": "für welches Rezept?",
"prstBtn": "Voreinstellungen", "prstBtn": "Voreinstellungen",
@ -308,17 +308,17 @@
"minute": "Minute", "minute": "Minute",
"dlyDur": "Verzögerungsdauer", "dlyDur": "Verzögerungsdauer",
"delay": "Verzögerung", "delay": "Verzögerung",
"tmrRm": "Eieruhr entfernt", "tmrRm": "Kurzzeitwecker entfernt",
"notifSetg": "Benachrichtigungseinstellungen", "notifSetg": "Benachrichtigungseinstellungen",
"delPrst": "Du bist dabei, %s aus den Voreinstellungen zu löschen", "delPrst": "Du bist dabei, %s aus den Voreinstellungen zu löschen",
"prsts": "Voreinstellungen", "prsts": "Voreinstellungen",
"ttv": "Zum Anzeigen tippen", "ttv": "Zum Anzeigen tippen",
"dismissAll": "Alle Eieruhren verwerfen", "dismissAll": "Alle Kurzzeitwecker verwerfen",
"dismiss": "Verwerfen", "dismiss": "Verwerfen",
"texp": "%s Eieruhren abgelaufen", "texp": "%s Kurzzeitwecker abgelaufen",
"wDBy": "%1$s wurde um %2$s verzögert", "wDBy": "%1$s wurde um %2$s verzögert",
"prstTU": "Voreingestellte Zeit aktualisiert", "prstTU": "Voreingestellte Zeit aktualisiert",
"ccwt": "Koche selbstbewusst mit Eieruhren!", "ccwt": "Koche selbstbewusst mit Kurzzeitweckern!",
"gtD": "Zu Datum gehen", "gtD": "Zu Datum gehen",
"random": "Zufällig", "random": "Zufällig",
"oAP": "%1$s laufend, %2$s angehalten", "oAP": "%1$s laufend, %2$s angehalten",
@ -333,5 +333,16 @@
"tue": "Dienstag", "tue": "Dienstag",
"mon": "Montag", "mon": "Montag",
"sun": "Sonntag", "sun": "Sonntag",
"calVM": "Modus der Kalenderansicht" "calVM": "Modus der Kalenderansicht",
"d": "Tag",
"wk": "Woche",
"mnth": "Monat",
"nvr": "Nie",
"otaw": "Älter als eine Woche",
"otam": "Älter als ein Monat",
"otay": "Älter als ein Jahr",
"admp": "Mahlzeitenpläne automatisch löschen",
"plsCrt": "Verwende die Plus-Taste, um einen zu erstellen",
"ehwmp": "Iss gesund mit Essensplänen!",
"selMT": "Mahlzeitentyp auswählen"
} }

View file

@ -336,5 +336,14 @@
"cpy": "copy", "cpy": "copy",
"tdy": "Today", "tdy": "Today",
"tmrw": "Tomorrow", "tmrw": "Tomorrow",
"ystr": "Yesterday" "ystr": "Yesterday",
"selMT": "Select meal type",
"ehwmp": "Eat healthy with meal plans!",
"plsCrt": "Use the plus button to create one",
"admp": "Auto-delete meal plans",
"otay": "Older than a year",
"otam": "Older than a month",
"otaw": "Older than a week",
"nvr": "Never",
"add": "Add"
} }

View file

@ -333,5 +333,16 @@
"mon": "Monday", "mon": "Monday",
"sun": "Sunday", "sun": "Sunday",
"calVM": "Calendar view mode", "calVM": "Calendar view mode",
"oAP": "%1$s ongoing, %2$s paused" "oAP": "%1$s ongoing, %2$s paused",
"d": "Day",
"wk": "Week",
"mnth": "Month",
"nvr": "Never",
"otaw": "Older than a week",
"otam": "Older than a month",
"otay": "Older than a year",
"admp": "Auto delete meal plans",
"plsCrt": "Use the plus button to create one",
"ehwmp": "Eat healthy with meal plans!",
"selMT": "Select meal type"
} }

View file

@ -333,5 +333,16 @@
"mon": "Monday", "mon": "Monday",
"sun": "Sunday", "sun": "Sunday",
"calVM": "Calendar view mode", "calVM": "Calendar view mode",
"oAP": "%1$s ongoing, %2$s paused" "oAP": "%1$s ongoing, %2$s paused",
"d": "Day",
"wk": "Week",
"mnth": "Month",
"nvr": "Never",
"otaw": "Older than a week",
"otam": "Older than a month",
"otay": "Older than a year",
"admp": "Auto delete meal plans",
"plsCrt": "Use the plus button to create one",
"ehwmp": "Eat healthy with meal plans!",
"selMT": "Select meal type"
} }

View file

@ -312,14 +312,6 @@
"tmrRm": "Temporizador eliminado", "tmrRm": "Temporizador eliminado",
"delPrst": "Está a punto de eliminar %s de los preajustes", "delPrst": "Está a punto de eliminar %s de los preajustes",
"prsts": "Preselecciones", "prsts": "Preselecciones",
"ttv": "Toca para ver",
"dismissAll": "Descartar todos los temporizadores",
"dismiss": "Descartar",
"texp": "%s temporizadores expirados",
"random": "Aleatorio",
"ystr": "Ayer",
"tmrw": "Mañana",
"tdy": "Hoy",
"cpy": "copia", "cpy": "copia",
"sat": "sábado", "sat": "sábado",
"fri": "viernes", "fri": "viernes",
@ -328,9 +320,10 @@
"tue": "martes", "tue": "martes",
"mon": "lunes", "mon": "lunes",
"sun": "domingo", "sun": "domingo",
"calVM": "Modo de vista del calendario", "d": "Día",
"oAP": "%1$s en curso, %2$s en pausa", "wk": "Semana",
"prstTU": "Hora preestablecida actualizada", "mnth": "Mes",
"ccwt": "¡Cocine con confianza con los temporizadores!", "ystr": "Ayer",
"gtD": "Ir a la fecha" "tmrw": "Mañana",
"tdy": "Hoy"
} }

343
app/i18n/fi.json Normal file
View file

@ -0,0 +1,343 @@
{
"aap": "Liitä valokuva",
"About": "Tietoja",
"aBtn": "LISÄÄ",
"aD": "Valmista!",
"addCmbBtn": "LISÄÄ YHDISTELMÄ",
"aFBu": "Lisää resepti varmuuskopioinnin suorittamiseen",
"aIngBtn": "LISÄÄ AINESOSA",
"allCats": "Kaikki luokat",
"allCuis": "Kaikki keittiöt",
"allTs": "Kaikki tunnisteet",
"American": "amerikkalainen",
"aNBtn": "LISÄÄ UUSI",
"aNoBtn": "LISÄÄ HUOMAUTUS",
"app.name": "EnRecipes",
"appCrd": "Jaettu EnRecipesin kautta. Lataa se F-Droidista, IzzyOnDroidista tai Play Storesta.",
"Appetizers": "Alkupalat",
"appInfo": "EnRecipes on avoimen lähdekoodin, yksityisyyden suojaa kunnioittava digitaalinen keittokirja, jonka avulla voit luoda, hallita ja jakaa reseptejäsi",
"apply": "KÄYTÄ",
"appRst": "Sovelluksen uudelleenkäynnistys vaaditaan",
"April": "huhtikuu",
"aStpBtn": "",
"August": "elokuu",
"Barbecue": "Grilli",
"Beverages": "Juomat",
"Black": "Musta",
"Brazilian": "brasilialainen",
"Breads": "Leivät",
"breakfast": "Aamiainen",
"British": "brittiläinen",
"buEmp": "Varmuuskopiotiedosto on tyhjä",
"buFol": "Varmuuskopioiden hakemisto",
"buInc": "Virheellinen tai vioittunut varmuuskopiotiedosto",
"buInfo": "Luo ZIP-tiedoston, joka sisältää kaikki tiedot, jotka voidaan tuoda takaisin",
"buMod": "Varmuuskopiotiedostoa muutettiin muualla",
"cat": "Luokka",
"cBtn": "PERUUTA",
"Challenging": "Haastava",
"Chinese": "kiinalainen",
"clove": "kynsi",
"cm": "cm",
"cmbs": "Yhdistelmät",
"conBtn": "JATKA",
"conf": "Vahvista",
"cookT": "Kypsennysaika",
"cPic": "Rajaa kuva",
"Created": "Luotu",
"cui": "Keittiö",
"cup": "cup",
"Cup": "Kuppi",
"dAgo": "%s päivää sitten",
"Danish": "tanskalainen",
"Dark": "Tumma",
"db": "Tietokanta",
"dBtn": "POISTA",
"December": "joulukuu",
"delRecInfo": "Olet poistamassa reseptiä %s pysyvästi",
"delRecsInfo": "Olet poistamassa %s pysyvästi",
"Desserts": "Jälkiruoat",
"detailed": "Yksityiskohtainen",
"Difficulty level": "Vaikeustaso",
"dinner": "Illallinen",
"disBtn": "HYLKÄÄ",
"disc": "Tässä reseptissä on tallentamattomia muutoksia. Mitä haluaisit tehdä?",
"donate": "Lahjoita",
"dozen": "tusina",
"drop": "",
"dsp": "",
"Easy": "Helppo",
"editRec": "Muokkaa resepti",
"Egyptian": "egyptiläinen",
"English": "englantilainen",
"EnRecipes": "EnRecipes",
"expBu": "Vie koko varmuuskopio",
"expip": "Vienti käynnissä",
"expSuc": "",
"favourites": "",
"February": "helmikuu",
"Filipino": "",
"Filtered recipes": "Suodatetut reseptit",
"fl oz": "",
"fltr": "",
"Fluid Ounce": "",
"French": "ranskalainen",
"FRI": "pe",
"fsList": "Suosikkireseptisi on lueteltu täällä",
"g": "g",
"gal": "",
"Gallon": "",
"German": "saksalainen",
"gh": "Näytä GitHub:ssa",
"Gram": "gramma",
"Greek": "kreikkalainen",
"grid": "",
"grocery": "Ostoslista",
"guide": "Käyttöopas",
"Healthy": "Terveellinen",
"hr": "t.",
"impBu": "Tuo tietoja",
"impFail": "Tuonti epäonnistui",
"impInfo": "Tukee tämän sovelluksen viemiä täydellisiä varmuuskopioita",
"impip": "Tuonti käynnissä",
"impSuc": "Tuonti onnistui",
"in": "",
"Indian": "intialainen",
"ings": "Ainekset",
"inss": "Ohjeet",
"intf": "Käyttöliittymä",
"invFile": "Virheellinen tiedosto",
"Irish": "irlantilainen",
"it": "",
"Italian": "",
"Jamaican": "",
"January": "tammikuu",
"Japanese": "japanilainen",
"Jewish": "juutalainen",
"joinTG": "",
"July": "heinäkuu",
"June": "kesäkuu",
"kEdit": "",
"Kenyan": "",
"kg": "kg",
"Kilogram": "kilogramma",
"Korean": "korealainen",
"l": "l",
"lang": "Kieli",
"large": "",
"Last updated": "",
"lb": "",
"leaf": "",
"Light": "",
"listVM": "",
"Litre": "",
"Loaf": "",
"ltAgo": "",
"lunch": "",
"mAgo": "",
"Main dishes": "",
"March": "",
"May": "",
"Meat": "",
"medium": "",
"Mexican": "",
"mg": "",
"Millilitre": "",
"min": "",
"minimal": "",
"ml": "",
"Moderate": "",
"MON": "",
"newCui": "",
"Newest first": "",
"newRec": "",
"newUnit": "",
"Nigerian": "",
"nLangInfo": "",
"nNBtn": "",
"no": "",
"noAccSensor": "",
"noFavs": "",
"Noodles": "",
"noRecs": "Hakua vastaavia reseptejä ei ole",
"noRecsInL": "",
"nos": "",
"November": "marraskuu",
"nwCat": "Uusi luokka",
"nwYiU": "",
"October": "lokakuu",
"OK": "OK",
"Oldest first": "",
"opts": "Valinnat",
"Ounce": "",
"oz": "",
"Pasta": "",
"Patty": "",
"photogrid": "",
"pht": "Reseptivalokuva",
"piece": "pala",
"Piece": "Pala",
"pinch": "",
"planner": "Ateriasuunnittelija",
"plsAdd": "Lisää resepti pluspainikkeella",
"Portuguese": "portugalilainen",
"Poultry": "",
"Pound": "",
"prepT": "Valmisteluaika",
"priv": "Tietosuojakäytäntö",
"pt": "",
"qt": "",
"Quickest first": "",
"Rating": "",
"rBtn": "POISTA",
"rec": "Resepti",
"recE": "On jo olemassa:",
"recF": "löydettyä reseptiä",
"recI": "",
"recListEmp": "",
"recPic": "Reseptivalokuva",
"recRm": "Resepti poistettu",
"recs": "reseptiä",
"recTitle": "Minun terveellinen reseptini",
"recU": "Päivitetty:",
"req": "",
"resNF": "",
"rest": "Nollaa",
"restCatL": "",
"restCuiL": "",
"restDone": "",
"restInfo": "",
"restUL": "",
"restYUL": "",
"Rice": "Riisi",
"rmCatInfo": "Olet poistamassa %s luokkaluettelosta",
"rmCmb": "Yhdistelmä poistettu",
"rmCuiInfo": "",
"rmIng": "Ainesosa poistettu",
"rmIns": "Ohje poistettu",
"rmN": "Huomautus poistettu",
"rmUInfo": "",
"rmYUInfo": "",
"Roll": "",
"rp": "Poista valokuva",
"rst": "UUDELLEENKÄYNNISTÄ",
"Russian": "venäläinen",
"Salads": "Salaatit",
"SAT": "la",
"Sauces": "Kastikkeet",
"Scottish": "skotlantilainen",
"Seafood": "",
"selRec": "",
"September": "",
"ser": "Etsi",
"Serving": "",
"SET": "ASETA",
"Settings": "Asetukset",
"shr": "Jaa",
"Side dishes": "",
"simple": "",
"Slowest first": "",
"sltd": "",
"small": "",
"snacks": "",
"Soups": "Keitot",
"Spanish": "espanjalainen",
"Sri Lankan": "srilankalainen",
"srpu": "Jaa reseptikuva käyttämällä…",
"srt": "Järjestä",
"sru": "Jaa resepti käyttämällä…",
"stars": "",
"stick": "",
"stp": "",
"strAdd": "",
"SUN": "su",
"sVw": "Ravista nähdäksesi satunnainen resepti",
"sVwInfo": "Auttaa sinua valitsemaan, mitä valmistaa, kun et osaa päättää",
"Swedish": "ruotsalainen",
"swm": "Aloita viikko maanantaina",
"sysDef": "Järjestelmän oletus",
"Tablespoon": "",
"tbsp": "",
"Teaspoon": "",
"Thai": "thaimaalainen",
"Theme": "Teema",
"THU": "to",
"title": "Otsikko",
"tLInfo": "Reseptit, joita haluat kokeilla myöhemmin, on lueteltu täällä",
"today": "tänään",
"triedInfo": "Kokeilit tätä reseptiä %s",
"trnsl": "Käännä",
"trylater": "",
"trySer": "ETSI KAIKISTA RESEPTEISTÄ?",
"ts": "Tunnisteet",
"tsInfo": "",
"tsp": "",
"TUE": "ti",
"Turkish": "",
"Undefined": "Määrittelemätön",
"unit": "",
"Unit": "",
"unsaved": "",
"untRec": "",
"Vegan": "Vegaani",
"Vegetarian": "Kasvissyöjä",
"Vietnamese": "vietnamilainen",
"wAgo": "%s viikkoa sitten",
"WED": "ke",
"yesterday": "eilen",
"yieldQ": "",
"yieldU": "",
"yld": "",
"buto": "",
"sysDefB": "Järjestelmän oletusarvo + Musta",
"tmr": "Ajastin %s",
"stop": "Pysäytä",
"ntmr": "Uusi ajastin",
"timer": "Keittoajastin",
"tmrSnd": "Ajastinääni",
"aTPrst": "Lisätty esiasetuksiin",
"fwr": "mitä reseptiä varten?",
"prstBtn": "ESIASETUKSET",
"sec": "s",
"dlyDur": "Viiveen kesto",
"seconds": "sekuntia",
"hours": "tuntia",
"minutes": "minuuttia",
"hour": "tunti",
"minute": "minuutti",
"prsts": "Esiasetukset",
"texp": "%s ajastinta on vanhentunut",
"tmrRm": "Ajastin poistettu",
"ccwt": "Keitä luottavaisesti ajastimien avulla!",
"gtD": "Siirry päivämäärään",
"random": "Satunnainen",
"notifSetg": "Ilmoitusasetukset",
"prstTU": "Esiasetettu aika päivitetty",
"ttv": "Napauta nähdäksesi",
"dismissAll": "Hylkää kaikki ajastimet",
"dismiss": "Hylkää",
"d": "Päivä",
"wk": "Viikko",
"mnth": "Kuukausi",
"ystr": "Eilen",
"tmrw": "Huomenna",
"tdy": "Tänään",
"cpy": "kopio",
"sat": "lauantai",
"fri": "perjantai",
"thu": "torstai",
"wed": "keskiviikko",
"tue": "tiistai",
"mon": "maanantai",
"sun": "sunnuntai",
"calVM": "Kalenterinäkymätila",
"oAP": "%1$s käynnissä, %2$s keskeytetty",
"ehwmp": "Syö terveellisesti ateriasuunnitelmien avulla!",
"selMT": "Valitse ateriatyyppi",
"plsCrt": "Käytä plus-painiketta luodaksesi yhden",
"nvr": "Ei koskaan",
"otaw": "Viikon jälkeen",
"otam": "Kuukauden jälkeen",
"otay": "Vuoden jälkeen",
"admp": "Poista ateriasuunnitelmat automaattisesti"
}

View file

@ -323,7 +323,7 @@
"random": "Aléatoire", "random": "Aléatoire",
"ystr": "Hier", "ystr": "Hier",
"tmrw": "Demain", "tmrw": "Demain",
"tdy": "Oggi", "tdy": "Aujourdhui",
"cpy": "copie", "cpy": "copie",
"sat": "samedi", "sat": "samedi",
"fri": "vendredi", "fri": "vendredi",
@ -332,6 +332,17 @@
"tue": "mardi", "tue": "mardi",
"mon": "lundi", "mon": "lundi",
"sun": "dimanche", "sun": "dimanche",
"d": "Jour",
"wk": "Semaine",
"mnth": "Mois",
"calVM": "Mode daffichage du calendrier", "calVM": "Mode daffichage du calendrier",
"oAP": "%1$s en cours, %2$s en pause" "oAP": "%1$s en cours, %2$s en pause",
"otaw": "Après une semaine",
"otam": "Après un mois",
"otay": "Après un an",
"admp": "Supprimer automatiquement les planifications de repas",
"nvr": "Jamais",
"plsCrt": "Utilisez le bouton plus pour en créer une",
"ehwmp": "Mangez sainement grâce aux planifications de repas !",
"selMT": "Sélectionnez le type de repas"
} }

View file

@ -321,9 +321,9 @@
"ccwt": "Cuisinez avec confiance avec des minuteries!", "ccwt": "Cuisinez avec confiance avec des minuteries!",
"gtD": "Aller à la date", "gtD": "Aller à la date",
"random": "Aléatoire", "random": "Aléatoire",
"tdy": "Aujourdhui",
"ystr": "Hier", "ystr": "Hier",
"tmrw": "Demain", "tmrw": "Demain",
"tdy": "Aujourdhui",
"cpy": "copie", "cpy": "copie",
"sat": "samedi", "sat": "samedi",
"fri": "vendredi", "fri": "vendredi",
@ -332,6 +332,17 @@
"tue": "mardi", "tue": "mardi",
"mon": "lundi", "mon": "lundi",
"sun": "dimanche", "sun": "dimanche",
"d": "Jour",
"wk": "Semaine",
"mnth": "Mois",
"calVM": "Mode daffichage du calendrier", "calVM": "Mode daffichage du calendrier",
"oAP": "%1$s en cours, %2$s en pause" "oAP": "%1$s en cours, %2$s en pause",
"otaw": "Après une semaine",
"otam": "Après un mois",
"otay": "Après un an",
"admp": "Supprimer automatiquement les planifications de repas",
"nvr": "Jamais",
"plsCrt": "Utilisez le bouton plus pour en créer une",
"ehwmp": "Mangez sainement grâce aux planifications de repas!",
"selMT": "Sélectionnez le type de repas"
} }

View file

@ -323,7 +323,7 @@
"random": "Aléatoire", "random": "Aléatoire",
"ystr": "Hier", "ystr": "Hier",
"tmrw": "Demain", "tmrw": "Demain",
"tdy": "Oggi", "tdy": "Aujourdhui",
"cpy": "copie", "cpy": "copie",
"sat": "samedi", "sat": "samedi",
"fri": "vendredi", "fri": "vendredi",
@ -332,6 +332,17 @@
"tue": "mardi", "tue": "mardi",
"mon": "lundi", "mon": "lundi",
"sun": "dimanche", "sun": "dimanche",
"d": "Jour",
"wk": "Semaine",
"mnth": "Mois",
"calVM": "Mode daffichage du calendrier", "calVM": "Mode daffichage du calendrier",
"oAP": "%1$s en cours, %2$s en pause" "oAP": "%1$s en cours, %2$s en pause",
"nvr": "Jamais",
"otaw": "Après une semaine",
"otam": "Après un mois",
"otay": "Après un an",
"admp": "Supprimer automatiquement les planifications de repas",
"plsCrt": "Utilisez le bouton plus pour en créer une",
"ehwmp": "Mangez sainement grâce aux planifications de repas!",
"selMT": "Sélectionnez le type de repas"
} }

View file

@ -321,9 +321,9 @@
"ccwt": "Cuisinez avec confiance avec des minuteries !", "ccwt": "Cuisinez avec confiance avec des minuteries !",
"gtD": "Aller à la date", "gtD": "Aller à la date",
"random": "Aléatoire", "random": "Aléatoire",
"tdy": "Aujourdhui",
"ystr": "Hier", "ystr": "Hier",
"tmrw": "Demain", "tmrw": "Demain",
"tdy": "Aujourdhui",
"cpy": "copie", "cpy": "copie",
"sat": "samedi", "sat": "samedi",
"fri": "vendredi", "fri": "vendredi",
@ -332,6 +332,17 @@
"tue": "mardi", "tue": "mardi",
"mon": "lundi", "mon": "lundi",
"sun": "dimanche", "sun": "dimanche",
"d": "Jour",
"wk": "Semaine",
"mnth": "Mois",
"calVM": "Mode daffichage du calendrier", "calVM": "Mode daffichage du calendrier",
"oAP": "%1$s en cours, %2$s en pause" "oAP": "%1$s en cours, %2$s en pause",
"otaw": "Après une semaine",
"otam": "Après un mois",
"otay": "Après un an",
"admp": "Supprimer automatiquement les planifications de repas",
"nvr": "Jamais",
"plsCrt": "Utilisez le bouton plus pour en créer une",
"ehwmp": "Mangez sainement grâce aux planifications de repas !",
"selMT": "Sélectionnez le type de repas"
} }

View file

@ -332,6 +332,17 @@
"tue": "martedì", "tue": "martedì",
"mon": "lunedì", "mon": "lunedì",
"sun": "domenica", "sun": "domenica",
"d": "Giorno",
"wk": "Settimana",
"mnth": "Mese",
"calVM": "Modalità di visualizzazione calendario", "calVM": "Modalità di visualizzazione calendario",
"oAP": "%1$s in corso, %2$s in pausa" "oAP": "%1$s in corso, %2$s in pausa",
"otaw": "Dopo una settimana",
"otam": "Dopo un mese",
"otay": "Dopo un anno",
"nvr": "Mai",
"admp": "Elimina automaticamente i piani pasto",
"plsCrt": "Usa il pulsante più per crearne uno",
"ehwmp": "Mangia sano con i piani dei pasti!",
"selMT": "Seleziona il tipo di pasto"
} }

293
app/i18n/ja.json Normal file
View file

@ -0,0 +1,293 @@
{
"aap": "写真を添付",
"About": "アプリについて",
"aBtn": "追加",
"aD": "完了しました!",
"addCmbBtn": "組合せを追加",
"aFBu": "バックアップするにはレシピを登録してください",
"aIngBtn": "材料を追加",
"allCats": "全てのカテゴリー",
"allCuis": "全てのジャンル",
"allTs": "全てのタグ",
"American": "アメリカ料理",
"aNBtn": "新しく追加",
"aNoBtn": "メモを追加",
"app.name": "EnRecipes",
"appCrd": "EnRecipesから共有されました。F-DroidかIzzyOnDroidかPlayストアからダウンロード出来ます。",
"Appetizers": "前菜",
"appInfo": "EnRecipesはレシピを作成・管理・共有することができる、オープンソースでプライバシーに配慮したデジタル料理本です",
"apply": "適用する",
"appRst": "アプリの再起動が必要",
"April": "4月",
"aStpBtn": "手順を追加",
"August": "8月",
"Barbecue": "キャンプ料理",
"Beverages": "飲物",
"Black": "黒",
"Brazilian": "ブラジル料理",
"Breads": "パン",
"breakfast": "朝食",
"British": "イギリス料理",
"buEmp": "バックアップファイルが空です",
"buFol": "バックアップフォルダー",
"buInc": "バックアップファイルが壊れています",
"buInfo": "全てのデータを復元するためのZIPファイルを生成します",
"buMod": "バックアップファイルが一部修正されています",
"cat": "カテゴリー",
"cBtn": "キャンセル",
"Challenging": "難しい",
"Chinese": "中華料理",
"clove": "クローブ",
"cm": "cm",
"cmbs": "組合せ",
"conBtn": "続ける",
"conf": "確認",
"cookT": "調理時間",
"cPic": "写真を切り抜く",
"Created": "作成日時",
"cui": "ジャンル",
"cup": "カップ",
"Cup": "カップ",
"dAgo": "%s 日前",
"Danish": "デンマーク料理",
"Dark": "ダーク",
"db": "データベース",
"dBtn": "削除",
"December": "12月",
"delRecInfo": "レシピ %s を削除しようとしています",
"delRecsInfo": "%s 件のレシピを削除しようとしています",
"Desserts": "デザート",
"detailed": "詳細",
"Difficulty level": "難易度",
"dinner": "夕食",
"disBtn": "破棄",
"disc": "レシピの変更が保存されていません。どうしますか?",
"donate": "寄付する",
"dozen": "ダース",
"drop": "滴",
"dsp": "デザート用スプーン",
"Easy": "簡単",
"editRec": "レシピを編集する",
"Egyptian": "エジプト料理",
"English": "イングランド料理",
"EnRecipes": "EnRecipes",
"expBu": "バックアップをエクスポート",
"expip": "エクスポートを実行中",
"expSuc": "エクスポートに成功",
"favourites": "お気に入り",
"February": "2月",
"Filipino": "フィリピン料理",
"Filtered recipes": "絞り込み結果",
"fl oz": "液量オンス",
"fltr": "絞り込み",
"Fluid Ounce": "液量オンス",
"French": "フランス料理",
"FRI": "金",
"fsList": "お気に入りのレシピはここに表示されます",
"g": "g",
"gal": "ガロン",
"Gallon": "ガロン",
"German": "ドイツ料理",
"gh": "GitHubで見る",
"Gram": "グラム",
"Greek": "ギリシャ料理",
"grid": "グリッド",
"grocery": "食材のリスト",
"guide": "ユーザーガイド",
"Healthy": "ヘルシー",
"hr": "時間",
"impBu": "データをインポート",
"impFail": "インポートに失敗",
"impInfo": "アプリからエクスポートしたフルバックアップに対応しています",
"impip": "インポートを実行中",
"impSuc": "インポートに成功",
"Indian": "インド料理",
"ings": "材料",
"inss": "作り方",
"intf": "インターフェイス",
"invFile": "無効なファイルです",
"Irish": "アイルランド料理",
"Italian": "イタリア料理",
"Jamaican": "ジャマイカ料理",
"January": "1月",
"Japanese": "和食",
"Jewish": "ユダヤ料理",
"joinTG": "Telegramグループに参加",
"July": "7月",
"June": "6月",
"kEdit": "編集を続ける",
"Kenyan": "ケニア料理",
"kg": "kg",
"Kilogram": "キログラム",
"Korean": "韓国料理",
"l": "リットル",
"lang": "言語",
"Last updated": "最終更新",
"leaf": "枚",
"Light": "ライト",
"listVM": "一覧の表示方法",
"Litre": "リットル",
"Loaf": "ローフ",
"ltAgo": "ずっと前",
"lunch": "昼食",
"mAgo": "%s か月前",
"Main dishes": "主菜",
"March": "3月",
"May": "5月",
"Meat": "肉料理",
"Mexican": "メキシコ料理",
"mg": "mg",
"Millilitre": "ミリリットル",
"min": "分",
"minimal": "最小",
"ml": "ml",
"Moderate": "普通",
"MON": "月",
"newCui": "新しいジャンル",
"Newest first": "新しい順",
"newRec": "新しいレシピ",
"newUnit": "新しい単位",
"Nigerian": "ナイジェリア料理",
"nLangInfo": "新しい言語を使用するためにEnRecipesを再起動",
"no": "メモ",
"noAccSensor": "加速度センサーが無効です",
"noFavs": "お気に入りはまだありません",
"Noodles": "麺",
"noRecs": "検索に一致するレシピがありません",
"noRecsInL": "検索に一致するレシピが見つかりませんでした",
"nos": "メモ",
"November": "11月",
"nwCat": "新しいカテゴリー",
"nwYiU": "新しい仕上がり単位",
"October": "10月",
"OK": "OK",
"Oldest first": "古いもの順",
"opts": "オプション",
"Ounce": "オンス",
"Pasta": "パスタ",
"Patty": "パティ",
"photogrid": "写真グリッド",
"pht": "レシピの写真",
"piece": "片",
"Piece": "片",
"planner": "献立プラン",
"plsAdd": "プラスボタンを押して追加します",
"Portuguese": "ポルトガル料理",
"Poultry": "チキン料理",
"Pound": "ポンド",
"prepT": "準備時間",
"priv": "プライバシーポリシー",
"pt": "少々",
"qt": "クォート",
"Quickest first": "速いもの順",
"Rating": "評価",
"rBtn": "削除",
"rec": "レシピ",
"recE": "既に存在します:",
"recF": "件のレシピが見つかりました",
"recI": "インポートしました:",
"recListEmp": "まだ何もありません!レシピを追加してもう一度試してください",
"recPic": "レシピの写真",
"recRm": "レシピを削除しました",
"recs": "レシピ",
"recTitle": "私の健康レシピ",
"recU": "アップデート:",
"req": "%s が必要",
"resNF": "レシピが見つかりません",
"rest": "リセット",
"restCatL": "カテゴリーリストをリセット",
"restCuiL": "ジャンルのリストをリセット",
"restDone": "リセットが完了しました",
"restInfo": "リストをリセットすると、作成した項目が削除され、既定の項目を復元します。既にあるレシピには影響しません。",
"restUL": "単位のリストをリセットする",
"restYUL": "仕上がり分量の単位のリストをリセットする",
"Rice": "お米料理",
"rmCatInfo": "カテゴリーのリストから %s を削除しようとしています",
"rmCmb": "組合せが削除されました",
"rmCuiInfo": "ジャンルのリストから %s を削除しようとしています",
"rmIng": "材料を削除しました",
"rmIns": "作り方を削除しました",
"rmN": "メモが削除されました",
"rmUInfo": "単位のリストから %s を削除しようとしています",
"rmYUInfo": "仕上がり分量の単位のリストから %s を削除しようとしています",
"Roll": "ロール",
"rp": "写真を削除",
"rst": "再起動",
"Russian": "ロシア料理",
"Salads": "サラダ",
"SAT": "土",
"Sauces": "ソース",
"Scottish": "スコットランド料理",
"Seafood": "シーフード",
"selRec": "レシピを選択",
"September": "9月",
"ser": "検索",
"Serving": "人前",
"SET": "設定",
"Settings": "設定",
"shr": "共有",
"Side dishes": "副菜",
"simple": "シンプル",
"Slowest first": "遅い順",
"sltd": "選択",
"snacks": "軽食",
"Soups": "スープ",
"Spanish": "スペイン料理",
"Sri Lankan": "スリランカ料理",
"srpu": "レシピの写真を共有...",
"srt": "並び替え",
"sru": "レシピを共有...",
"stars": "星評価",
"stp": "作り方",
"strAdd": "レシピを追加しましょう!",
"SUN": "日",
"sVw": "振ってランダムなレシピを表示",
"sVwInfo": "何を作るか決められないときに使います",
"Swedish": "スウェーデン料理",
"swm": "月曜始まりにする",
"sysDef": "システム既定",
"Tablespoon": "大さじ",
"tbsp": "大さじ",
"Teaspoon": "小さじ",
"Thai": "タイ料理",
"Theme": "テーマ",
"THU": "木",
"title": "タイトル",
"tLInfo": "あとで試したいレシピはここに並びます",
"today": "今日",
"triedInfo": "に作りました",
"trnsl": "翻訳",
"trylater": "あとで作る",
"trySer": "全てのレシピを検索しますか?",
"ts": "タグ",
"tsInfo": "スペースで区切ってください",
"tsp": "小さじ",
"TUE": "火",
"Turkish": "トルコ料理",
"unit": "個",
"Unit": "単位",
"unsaved": "未保存の変更",
"untRec": "無題のレシピ",
"Vegan": "ビーガン",
"Vegetarian": "ベジタリアン",
"Vietnamese": "ベトナム料理",
"wAgo": "%s 週前",
"WED": "水",
"yesterday": "昨日",
"yieldQ": "仕上がり分量",
"yieldU": "仕上がり分量の単位",
"yld": "仕上がり分量",
"buto": "%s としてバックアップしました",
"sysDefB": "システム既定+黒",
"in": "中の",
"Undefined": "未定義",
"nNBtn": "あとで",
"stick": "スティック",
"pinch": "ピンチ",
"oz": "オンス",
"lb": "ポンド",
"it": "材料",
"small": "小",
"large": "大",
"medium": "中"
}

293
app/i18n/ml.json Normal file
View file

@ -0,0 +1,293 @@
{
"aap": "ഒരു ഫോട്ടോ അറ്റാച്ചുചെയ്യുക",
"About": "കുറിച്ച്",
"aBtn": "ചേർക്കുക",
"aD": "എല്ലാം ചെയ്തു!",
"addCmbBtn": "സംയോജനം ചേർക്കുക",
"aFBu": "ഒരു ബാക്കപ്പ് നടത്താൻ ഒരു പാചകക്കുറിപ്പ് ചേർക്കുക",
"aIngBtn": "ചേരുവ ചേർക്കുക",
"allCats": "എല്ലാ വിഭാഗങ്ങളും",
"allCuis": "എല്ലാ പാചകരീതികളും",
"allTs": "എല്ലാ ടാഗുകളും",
"American": "അമേരിക്കൻ",
"aNBtn": "പുതിയത് ചേർക്കുക",
"aNoBtn": "കുറിപ്പ് ചേര്ക്കുക",
"app.name": "ൻന്റെസിപിഎസ്",
"appCrd": "എന്റെരെസിപീസ് വഴി പങ്കിട്ടു. എഫ്-ആൻഡ്രോയിഡ്, ഇസിഓൺഡ്രോയിഡ് അല്ലെങ്കിൽ പ്ലേ സ്റ്റോറിൽ ഇത് നേടുക.",
"Appetizers": "വിശപ്പ്",
"appInfo": "നിങ്ങളുടെ പാചകക്കുറിപ്പുകൾ സൃഷ്ടിക്കാനും നിയന്ത്രിക്കാനും പങ്കിടാനും അനുവദിക്കുന്ന ഒരു ഓപ്പൺ സോഴ്‌സ്, സ്വകാര്യത കേന്ദ്രീകരിച്ച ഡിജിറ്റൽ പാചകപുസ്തകമാണ് എന്റെരെസിപീസ്",
"apply": "പ്രയോഗിക്കുക",
"appRst": "അപ്ലിക്കേഷൻ പുനരാരംഭിക്കൽ ആവശ്യമാണ്",
"April": "ഏപ്രിൽ",
"aStpBtn": "നിർദ്ദേശം ചേർക്കുക",
"August": "ഓഗസ്റ്റ്",
"Barbecue": "ബാർബിക്യൂ",
"Beverages": "പാനീയങ്ങൾ",
"Black": "കറുപ്പ്",
"Brazilian": "ബ്രസീലിയൻ",
"Breads": "ബ്രെഡുകൾ",
"breakfast": "പ്രഭാതഭക്ഷണം",
"British": "ബ്രിട്ടീഷ്",
"buEmp": "ബാക്കപ്പ് ഫയൽ ശൂന്യമാണ്",
"buFol": "ബാക്കപ്പ് ഫോൾഡർ",
"buInc": "കേടായ അല്ലെങ്കിൽ കേടായ ബാക്കപ്പ് ഫയൽ",
"buInfo": "തിരികെ ഇറക്കുമതി ചെയ്യാൻ കഴിയുന്ന നിങ്ങളുടെ എല്ലാ ഡാറ്റയും അടങ്ങിയ ഒരു ZIP ഫയൽ സൃഷ്ടിക്കുന്നു",
"buMod": "ബാക്കപ്പ് ഫയൽ മറ്റെവിടെയെങ്കിലും പരിഷ്‌ക്കരിച്ചു",
"cat": "വിഭാഗം",
"cBtn": "റദ്ദാക്കുക",
"Challenging": "വെല്ലുവിളിനിറഞ്ഞ",
"Chinese": "ചൈനീസ്",
"clove": "ഗ്രാമ്പൂ",
"cm": "സെന്റിമീറ്റർ",
"cmbs": "സംയോജനങ്ങൾ",
"conBtn": "തുടരുക",
"conf": "സ്ഥിരീകരിക്കുക",
"cookT": "പാചക സമയം",
"cPic": "ഫോട്ടോ എഡിറ്റുചെയ്യുക",
"Created": "സൃഷ്ടിച്ചു",
"cui": "പാചകരീതി",
"cup": "കപ്പ്",
"Cup": "കപ്പ്",
"dAgo": "%s ദിവസം മുമ്പ്",
"Danish": "ഡാനിഷ്",
"Dark": "ഇരുണ്ടത്",
"db": "ഡാറ്റാബേസ്",
"dBtn": "ഇല്ലാതാക്കുക",
"December": "ഡിസംബർ",
"delRecInfo": "%s പാചകക്കുറിപ്പ് നിങ്ങൾ ശാശ്വതമായി ഇല്ലാതാക്കാൻ പോകുന്നു",
"delRecsInfo": "നിങ്ങൾ %s ശാശ്വതമായി ഇല്ലാതാക്കാൻ പോകുന്നു",
"Desserts": "മധുരപലഹാരങ്ങൾ",
"detailed": "വിശദമായ",
"Difficulty level": "വൈഷമ്യ നില",
"dinner": "അത്താഴം",
"disBtn": "നിരസിക്കുക",
"disc": "ഈ പാചകക്കുറിപ്പിൽ സംരക്ഷിക്കാത്ത മാറ്റങ്ങളുണ്ട്. നിങ്ങൾ എന്താണ് ചെയ്യാൻ ആഗ്രഹിക്കുന്നത്?",
"donate": "സംഭാവനചെയ്യുക",
"dozen": "ഡസൻ",
"drop": "തുള്ളി",
"dsp": "ഡെസേർട്ട് സ്പൂൺ",
"Easy": "എളുപ്പമാണ്",
"editRec": "പാചകക്കുറിപ്പ് എഡിറ്റുചെയ്യുക",
"Egyptian": "ഈജിപ്ഷ്യൻ",
"English": "ഇംഗ്ലീഷ്",
"EnRecipes": "ൻന്റെസിപിഎസ്",
"expBu": "പൂർണ്ണ ബാക്കപ്പ് എക്‌സ്‌പോർട്ടുചെയ്യുക",
"expip": "കയറ്റുമതി പുരോഗതിയിലാണ്",
"expSuc": "കയറ്റുമതി വിജയം",
"favourites": "പ്രിയങ്കരങ്ങൾ",
"February": "ഫെബ്രുവരി",
"Filipino": "ഫിലിപ്പിനോ",
"Filtered recipes": "ഫിൽട്ടർ ചെയ്ത പാചകക്കുറിപ്പുകൾ",
"fl oz": "ഫ്ലൂയിഡ് un ൺസ്",
"fltr": "ഫിൽട്ടർ ചെയ്യുക",
"Fluid Ounce": "ഫ്ലൂയിഡ് un ൺസ്",
"French": "ഫ്രഞ്ച്",
"FRI": "വെള്ളി",
"fsList": "നിങ്ങളുടെ പ്രിയപ്പെട്ട പാചകക്കുറിപ്പുകൾ ഇവിടെ പട്ടികപ്പെടുത്തിയിട്ടുണ്ട്",
"g": "ഗ്രാം",
"gal": "ഗാലൺ",
"Gallon": "ഗാലൺ",
"German": "ജർമ്മൻ",
"gh": "GitHub- ൽ കാണുക",
"Gram": "ഗ്രാം",
"Greek": "ഗ്രീക്ക്",
"grid": "ഗ്രിഡ്",
"grocery": "പലചരക്ക് പട്ടിക",
"guide": "ഉപയോക്തൃ ഗൈഡ്",
"Healthy": "ആരോഗ്യമുള്ള",
"hr": "മണിക്കൂർ",
"impBu": "ഡാറ്റ ഇറക്കുമതി ചെയ്യുക",
"impFail": "ഇറക്കുമതി പരാജയപ്പെട്ടു",
"impInfo": "ഈ അപ്ലിക്കേഷൻ എക്‌സ്‌പോർട്ടുചെയ്‌ത പൂർണ്ണ ബാക്കപ്പുകൾ പിന്തുണയ്‌ക്കുന്നു",
"impip": "ഇറക്കുമതി പുരോഗതിയിലാണ്",
"impSuc": "ഇറക്കുമതി വിജയം",
"in": "ഇഞ്ച്",
"Indian": "ഇന്ത്യൻ",
"ings": "ചേരുവകൾ",
"inss": "നിർദ്ദേശങ്ങൾ",
"intf": "ഇന്റർഫേസ്",
"invFile": "അസാധുവായ ഫയൽ",
"Irish": "ഐറിഷ്",
"it": "ഇനം",
"Italian": "ഇറ്റാലിയൻ",
"Jamaican": "ജമൈക്കൻ",
"January": "ജനുവരി",
"Japanese": "ജാപ്പനീസ്",
"Jewish": "ജൂതൻ",
"joinTG": "ടെലിഗ്രാം ഗ്രൂപ്പിൽ ചേരുക",
"July": "ജൂലൈ",
"June": "ജൂൺ",
"kEdit": "എഡിറ്റിംഗ് തുടരുക",
"Kenyan": "കെനിയൻ",
"kg": "കി. ഗ്രാം",
"Kilogram": "കിലോഗ്രാം",
"Korean": "കൊറിയൻ",
"l": "ലിറ്റർ",
"lang": "ഭാഷ",
"large": "വലുത്",
"Last updated": "അവസാനമായി പുതുക്കിയത്",
"lb": "പൗണ്ട്",
"leaf": "ഇല",
"Light": "പ്രകാശം",
"listVM": "പട്ടിക കാഴ്ച മോഡ്",
"Litre": "ലിറ്റർ",
"Loaf": "അപ്പം",
"ltAgo": "വളരെക്കാലം മുമ്പ്",
"lunch": "ഉച്ചഭക്ഷണം",
"mAgo": "%s മാസം മുമ്പ്",
"Main dishes": "പ്രധാന വിഭവങ്ങൾ",
"March": "മാർച്ച്",
"May": "മെയ്",
"Meat": "മാംസം",
"medium": "ഇടത്തരം",
"Mexican": "മെക്സിക്കൻ",
"mg": "മില്ലിഗ്രാം",
"Millilitre": "മില്ലിലിറ്റർ",
"min": "മിനിറ്റ്",
"minimal": "കുറഞ്ഞത്",
"ml": "മില്ലി",
"Moderate": "മിതത്വം",
"MON": "തിങ്കൾ",
"newCui": "പുതിയ പാചകരീതി",
"Newest first": "ആദ്യത്തെ പുതിയത്",
"newRec": "പുതിയ പാചകക്കുറിപ്പ്",
"newUnit": "പുതിയ യൂണിറ്റ്",
"Nigerian": "നൈജീരിയൻ",
"nLangInfo": "പുതിയ ഭാഷ ഉപയോഗിക്കുന്നതിന് എന്റെരെസിപീസ് പുനരാരംഭിക്കുക",
"nNBtn": "ഇപ്പോൾ വേണ്ട",
"no": "കുറിപ്പ്",
"noAccSensor": "ആക്‌സിലറോമീറ്റർ സെൻസർ പ്രവർത്തനരഹിതമാക്കി അല്ലെങ്കിൽ പ്രവർത്തിക്കുന്നില്ല",
"noFavs": "ഇതുവരെ പ്രിയപ്പെട്ട പാചകക്കുറിപ്പുകളൊന്നുമില്ല",
"Noodles": "നൂഡിൽസ്",
"noRecs": "പാചകക്കുറിപ്പുകളൊന്നും നിങ്ങളുടെ തിരയലുമായി പൊരുത്തപ്പെടുന്നില്ല",
"noRecsInL": "ഇവിടെയുള്ള പാചകങ്ങളൊന്നും നിങ്ങളുടെ തിരയലുമായി പൊരുത്തപ്പെടുന്നില്ല",
"nos": "കുറിപ്പുകൾ",
"November": "നവംബർ",
"nwCat": "പുതിയ വിഭാഗം",
"nwYiU": "പുതിയ വിളവ് യൂണിറ്റ്",
"October": "ഒക്ടോബർ",
"OK": "ശരി",
"Oldest first": "ആദ്യം പഴയത്",
"opts": "ഓപ്ഷനുകൾ",
"Ounce": "Un ൺസ്",
"oz": "oun ൺസ്",
"Pasta": "പാസ്ത",
"Patty": "പാറ്റി",
"photogrid": "ഫോട്ടോ ഗ്രിഡ്",
"pht": "പാചകക്കുറിപ്പ് ഫോട്ടോ",
"piece": "കഷണം",
"Piece": "പീസ്",
"pinch": "നുള്ള്",
"planner": "ഭക്ഷണ പ്ലാനർ",
"plsAdd": "ഒരെണ്ണം ചേർക്കാൻ പ്ലസ് ബട്ടൺ ഉപയോഗിക്കുക",
"Portuguese": "പോർച്ചുഗീസ്",
"Poultry": "കോഴി",
"Pound": "പൗണ്ട്",
"prepT": "തയ്യാറാക്കൽ സമയം",
"priv": "സ്വകാര്യതാ നയം",
"pt": "പിന്റ്",
"qt": "ക്വാർട്ട്",
"Quickest first": "ആദ്യ വേഗത്തിലുള്ളത്",
"Rating": "റേറ്റിംഗ്",
"rBtn": "നീക്കംചെയ്യുക",
"rec": "പാചകക്കുറിപ്പ്",
"recE": "നിലവിലുള്ളത്:",
"recF": "പാചകക്കുറിപ്പുകൾ കണ്ടെത്തി",
"recI": "ഇറക്കുമതി ചെയ്തത്:",
"recListEmp": "ഇവിടെ ഒന്നുമില്ല! കുറച്ച് പാചകക്കുറിപ്പുകൾ ചേർത്ത് വീണ്ടും ശ്രമിക്കുക",
"recPic": "പാചകക്കുറിപ്പ് ഫോട്ടോ",
"recRm": "പാചകക്കുറിപ്പ് നീക്കംചെയ്‌തു",
"recs": "പാചകക്കുറിപ്പുകൾ",
"recTitle": "എന്റെ ആരോഗ്യകരമായ പാചകക്കുറിപ്പ്",
"recU": "അപ്‌ഡേറ്റുചെയ്‌തത്:",
"req": "ആവശ്യമായ %s",
"resNF": "പാചകക്കുറിപ്പ് കണ്ടെത്തിയില്ല",
"rest": "പുനഃക്രമീകരിക്കുക",
"restCatL": "വിഭാഗ ലിസ്റ്റ് പുന .സജ്ജമാക്കുക",
"restCuiL": "പാചകരീതി പട്ടിക പുന .സജ്ജമാക്കുക",
"restDone": "പുന .സജ്ജീകരണം പൂർത്തിയായി",
"restInfo": "ഒരു ലിസ്റ്റ് പുന .സജ്ജമാക്കുന്നത് ഉപയോക്താവ് സൃഷ്ടിച്ച എൻ‌ട്രികൾ ഇല്ലാതാക്കുകയും സ്ഥിരസ്ഥിതി എൻ‌ട്രികൾ പുന restore സ്ഥാപിക്കുകയും ചെയ്യും. നിലവിലുള്ള പാചകത്തെ ബാധിക്കില്ല.",
"restUL": "യൂണിറ്റ് ലിസ്റ്റ് പുന .സജ്ജമാക്കുക",
"restYUL": "വിളവ് യൂണിറ്റ് ലിസ്റ്റ് പുന .സജ്ജമാക്കുക",
"Rice": "അരി",
"rmCatInfo": "നിങ്ങൾ കാറ്റഗറി ലിസ്റ്റിൽ നിന്ന് %s നീക്കംചെയ്യാൻ പോകുന്നു",
"rmCmb": "കോമ്പിനേഷൻ നീക്കംചെയ്‌തു",
"rmCuiInfo": "നിങ്ങൾ പാചക പട്ടികയിൽ നിന്ന് %s നീക്കംചെയ്യാൻ പോകുന്നു",
"rmIng": "ചേരുവ നീക്കംചെയ്‌തു",
"rmIns": "നിർദ്ദേശം നീക്കംചെയ്‌തു",
"rmN": "കുറിപ്പ് നീക്കംചെയ്‌തു",
"rmUInfo": "നിങ്ങൾ %s യെ യൂണിറ്റ് പട്ടികയിൽ നിന്ന് നീക്കംചെയ്യാൻ പോവുകയാണ്",
"rmYUInfo": "വിളവ് യൂണിറ്റ് പട്ടികയിൽ നിന്ന് നിങ്ങൾ %s നീക്കംചെയ്യാൻ പോകുന്നു",
"Roll": "റോൾ",
"rp": "ഫോട്ടോ നീക്കംചെയ്യുക",
"rst": "പുനരാരംഭിക്കുക",
"Russian": "റഷ്യൻ",
"Salads": "സലാഡുകൾ",
"SAT": "ശനി",
"Sauces": "സോസുകൾ",
"Scottish": "സ്കോട്ടിഷ്",
"Seafood": "കടൽ ഭക്ഷണം",
"selRec": "പാചകക്കുറിപ്പ് തിരഞ്ഞെടുക്കുക",
"September": "സെപ്റ്റംബർ",
"ser": "തിരയുക",
"Serving": "സേവിക്കുന്നു",
"SET": "സെറ്റ്",
"Settings": "ക്രമീകരണങ്ങൾ",
"shr": "പങ്കിടുക",
"Side dishes": "സൈഡ് വിഭവങ്ങൾ",
"simple": "ലളിതം",
"Slowest first": "ആദ്യ വേഗത",
"sltd": "തിരഞ്ഞെടുത്തു",
"small": "ചെറുത്",
"snacks": "ലഘുഭക്ഷണങ്ങൾ",
"Soups": "സൂപ്പ്",
"Spanish": "സ്പാനിഷ്",
"Sri Lankan": "ശ്രീലങ്കൻ",
"srpu": "പാചകക്കുറിപ്പ് ഫോട്ടോ പങ്കിടുക...",
"srt": "അടുക്കുക",
"sru": "പാചകക്കുറിപ്പ് പങ്കിടുക...",
"stars": "നക്ഷത്ര റേറ്റിംഗ്",
"stick": "വടി",
"stp": "ഘട്ടം",
"strAdd": "നിങ്ങളുടെ പാചകക്കുറിപ്പുകൾ ചേർക്കാൻ ആരംഭിക്കുക!",
"SUN": "ഞായ",
"sVw": "ക്രമരഹിതമായ പാചകക്കുറിപ്പ് കാണാൻ കുലുക്കുക",
"sVwInfo": "നിങ്ങൾക്ക് തീരുമാനിക്കാൻ കഴിയാത്തപ്പോൾ എന്താണ് പാചകം ചെയ്യേണ്ടതെന്ന് തിരഞ്ഞെടുക്കാൻ നിങ്ങളെ സഹായിക്കുന്നു",
"Swedish": "സ്വീഡിഷ്",
"swm": "ആഴ്ചയിലെ ആദ്യ ദിവസം തിങ്കളാഴ്ച",
"sysDef": "സിസ്റ്റം സ്ഥിരസ്ഥിതി",
"Tablespoon": "ടേബിൾസ്പൂൺ",
"tbsp": "ടേബിൾസ്പൂൺ",
"Teaspoon": "ടീസ്പൂൺ",
"Thai": "തായ്",
"Theme": "തീം",
"THU": "വ്യാഴം",
"title": "ശീർഷകം",
"tLInfo": "നിങ്ങൾ പിന്നീട് ശ്രമിക്കാൻ ആഗ്രഹിക്കുന്ന പാചകക്കുറിപ്പുകൾ ഇവിടെ പട്ടികപ്പെടുത്തിയിട്ടുണ്ട്",
"today": "ഇന്ന്",
"triedInfo": "നിങ്ങൾ %s ഈ പാചകക്കുറിപ്പ് പരീക്ഷിച്ചു",
"trnsl": "വിവർത്തനം ചെയ്യുക",
"trylater": "പിന്നീട് ശ്രമിക്കുക",
"trySer": "എല്ലാ പാചകക്കുറിപ്പുകളിലും തിരയണോ?",
"ts": "ടാഗുകൾ",
"tsInfo": "സ്‌പെയ്‌സുകൾ ഉപയോഗിച്ച് വേർതിരിക്കുക",
"tsp": "ടീസ്പൂൺ",
"TUE": "ചൊവ്വ",
"Turkish": "ടർക്കിഷ്",
"Undefined": "നിർവചിച്ചിട്ടില്ല",
"unit": "യൂണിറ്റ്",
"Unit": "യൂണിറ്റ്",
"unsaved": "സംരക്ഷിക്കാത്ത മാറ്റങ്ങൾ",
"untRec": "ശീർഷകമില്ലാത്ത പാചകക്കുറിപ്പ്",
"Vegan": "വെഗാൻ",
"Vegetarian": "വെജിറ്റേറിയൻ",
"Vietnamese": "വിയറ്റ്നാമീസ്",
"wAgo": "%s ആഴ്ച മുമ്പ്",
"WED": "ബുധൻ",
"yesterday": "ഇന്നലെ",
"yieldQ": "വിളവ് അളവ്",
"yieldU": "വിളവ് യൂണിറ്റ്",
"yld": "വരുമാനം",
"buto": "%s ലേക്ക് ബാക്കപ്പ് ചെയ്തു",
"sysDefB": "സിസ്റ്റം സ്ഥിരസ്ഥിതി + കറുപ്പ്"
}

View file

@ -332,6 +332,17 @@
"tue": "dinsdag", "tue": "dinsdag",
"mon": "maandag", "mon": "maandag",
"sun": "zondag", "sun": "zondag",
"d": "Dag",
"wk": "Week",
"mnth": "Maand",
"calVM": "Weergavemodus van kalender", "calVM": "Weergavemodus van kalender",
"oAP": "%1$s lopende; %2$s onderbroken" "oAP": "%1$s lopende; %2$s onderbroken",
"nvr": "Nooit",
"otaw": "Ouder dan een week",
"otam": "Ouder dan een maand",
"otay": "Ouder dan een jaar",
"admp": "Schema's automatisch verwijderen",
"plsCrt": "Druk op de plusknop om een schema toe te voegen",
"ehwmp": "Eet gezond met maaltijdschema's!",
"selMT": "Kies het soort maaltijd"
} }

View file

@ -311,20 +311,5 @@
"prsts": "Predefinições", "prsts": "Predefinições",
"delPrst": "Você está prestes a apagar %s das predefinições", "delPrst": "Você está prestes a apagar %s das predefinições",
"tmrRm": "Timer removido", "tmrRm": "Timer removido",
"notifSetg": "Configurações de notificação", "notifSetg": "Configurações de notificação"
"calVM": "Modo de visualização de calendário",
"oAP": "%1$s em curso, %2$s em pausa",
"gtD": "Ir à data",
"random": "Aleatório",
"ystr": "Ontem",
"tmrw": "Amanhã",
"tdy": "Hoje",
"cpy": "cópia",
"sat": "sábado",
"fri": "sexta-feira",
"thu": "quinta-feira",
"wed": "quarta-feira",
"tue": "terça-feira",
"mon": "segunda-feira",
"sun": "domingo"
} }

View file

@ -321,17 +321,28 @@
"ccwt": "Cozinhe com segurança com temporizadores!", "ccwt": "Cozinhe com segurança com temporizadores!",
"gtD": "Ir à data", "gtD": "Ir à data",
"random": "Aleatória", "random": "Aleatória",
"cpy": "cópia", "nvr": "Nunca",
"oAP": "%1$s em curso, %2$s em pausa", "otaw": "Mais antigas que 1 semana",
"calVM": "Modo de visualização de calendário", "otam": "Mais antigas que 1 mês",
"otay": "Mais antigas que 1 ano",
"admp": "Eliminar automaticamente planos de refeições",
"plsCrt": "Use o botão + para criar uma",
"ehwmp": "Coma saudavelmente com planificações de refeições!",
"selMT": "Selecionar tipo de refeição",
"ystr": "Ontem", "ystr": "Ontem",
"tmrw": "Amanhã", "tmrw": "Amanhã",
"tdy": "Hoje", "tdy": "Hoje",
"sat": "sábado", "cpy": "cópia",
"fri": "sexta-feira", "sat": "Sábado",
"thu": "quinta-feira", "fri": "Sexta-feira",
"wed": "quarta-feira", "thu": "Quinta-feira",
"tue": "terça-feira", "wed": "Quarta-feira",
"mon": "segunda-feira", "tue": "Terça-feira",
"sun": "domingo" "mon": "Segunda-feira",
"sun": "Domingo",
"d": "Dia",
"wk": "Semana",
"mnth": "Mês",
"calVM": "Modo de vista tipo calendário",
"oAP": "%1$s a decorrer, %2$s em pausa"
} }

View file

@ -333,5 +333,16 @@
"mon": "திங்கள்", "mon": "திங்கள்",
"sun": "ஞாயிறு", "sun": "ஞாயிறு",
"calVM": "கேலெண்டர் காட்சி முறை", "calVM": "கேலெண்டர் காட்சி முறை",
"oAP": "%1$s செயலில் உள்ளது,%2$s இடைநிறுத்தப்பட்டது" "oAP": "%1$s செயலில் உள்ளது,%2$s இடைநிறுத்தப்பட்டது",
"d": "நாள்",
"wk": "வாரம்",
"mnth": "மாதம்",
"nvr": "ஒருபோதும்",
"otaw": "ஒரு வாரத்திற்கும் மேலானது",
"otam": "ஒரு மாதத்திற்கும் மேலானது",
"otay": "ஒரு வருடத்திற்கும் மேலானது",
"admp": "உணவு திட்டங்களை தானாக நீக்கு",
"plsCrt": "ஒன்றை உருவாக்க பிளஸ் பொத்தானைப் பயன்படுத்தவும்",
"ehwmp": "உணவு திட்டங்களுடன் ஆரோக்கியமாக சாப்பிடுங்கள்!",
"selMT": "உணவு வகையைத் தேர்ந்தெடுக்கவும்"
} }

View file

@ -1,45 +1,43 @@
import Vue from 'nativescript-vue'
import store from './store'
import { import {
Application, Application,
AndroidApplication, AndroidApplication,
ApplicationSettings, ApplicationSettings,
Utils, Utils,
Device,
Color,
Frame, Frame,
} from '@nativescript/core' } from '@nativescript/core'
import { import { localize } from '@nativescript/localize'
localize,
androidLaunchEventLocalizationHandler,
} from '@nativescript/localize'
import Vue from 'nativescript-vue'
import EnRecipes from './components/EnRecipes.vue' import EnRecipes from './components/EnRecipes.vue'
import EditRecipe from './components/EditRecipe.vue' import EditRecipe from './components/EditRecipe.vue'
import MealPlanner from './components/MealPlanner.vue' import MealPlanner from './components/MealPlanner.vue'
import CookingTimer from './components/CookingTimer.vue' import CookingTimer from './components/CookingTimer.vue'
import GroceryList from './components/GroceryList.vue'
import store from './store' // import GroceryList from './components/GroceryList.vue'
import * as utils from '~/shared/utils' import * as utils from '~/shared/utils'
export const EventBus = new Vue() export const EvtBus = new Vue()
let renderView: any = EnRecipes let renderView = EnRecipes
import CollectionView from '@nativescript-community/ui-collectionview/vue' import CollectionView from '@nativescript-community/ui-collectionview/vue'
Vue.use(CollectionView) Vue.use(CollectionView)
import { StackLayout, GridLayout, DockLayout } from '@nativescript-rtl/ui'
Vue.registerElement('RStackLayout', () => StackLayout) import { RGridLayout, RStackLayout, RDockLayout, RLabel } from './rtl-ui'
Vue.registerElement('RGridLayout', () => GridLayout) Vue.registerElement('RGridLayout', () => RGridLayout)
Vue.registerElement('RDockLayout', () => DockLayout) Vue.registerElement('RStackLayout', () => RStackLayout)
Vue.registerElement('RDockLayout', () => RDockLayout)
Vue.registerElement('RLabel', () => RLabel)
import { myMixin } from './shared/mixins' import { myMixin } from './shared/mixins'
Vue.mixin(myMixin) Vue.mixin(myMixin)
const initFrame = () => { const initFrame = () => {
const vm = store const vm = store
// MainInit
//MAIN INIT vm.commit('setTheme', ApplicationSettings.getString('theme', 'sysDef'))
vm.commit('setTheme', ApplicationSettings.getString('appTheme', 'sysDef')) vm.commit('initRecipes')
if (!vm.state.recipes.length) vm.commit('initRecipes')
vm.commit('initMealPlans') vm.commit('initMealPlans')
vm.commit('initListItems') vm.commit('initListItems')
vm.commit('initTimerPresets') vm.commit('initTimerPresets')
@ -50,42 +48,11 @@ const initFrame = () => {
hasTimerSound ? JSON.parse(hasTimerSound) : utils.getTones().defaultTone hasTimerSound ? JSON.parse(hasTimerSound) : utils.getTones().defaultTone
) )
} }
// InitFrame
// INIT FRAME
const View = android.view.View as any
const window = Application.android.startActivity.getWindow() const window = Application.android.startActivity.getWindow()
const decorView = window.getDecorView() const decorView = window.getDecorView()
let sdkv = parseInt(Device.sdkVersion) utils.setBarColors(window, decorView, vm.state.theme)
function setColors(color) { Frame.topmost().className = vm.state.theme
window.setStatusBarColor(new Color(color).android)
sdkv >= 27 && window.setNavigationBarColor(new Color(color).android)
}
switch (vm.state.appTheme) {
case 'Light':
setColors('#f1f3f5')
break
case 'Dark':
setColors('#212529')
break
default:
setColors('#000000')
break
}
if (sdkv >= 27)
decorView.setSystemUiVisibility(
vm.state.appTheme == 'Light'
? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR |
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
: View.SYSTEM_UI_FLAG_DARK_STATUS_BAR |
View.SYSTEM_UI_FLAG_DARK_NAVIGATION_BAR
)
else
decorView.setSystemUiVisibility(
vm.state.appTheme == 'Light'
? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
: View.SYSTEM_UI_FLAG_DARK_STATUS_BAR
)
Frame.topmost().className = store.state.appTheme
} }
const showOverLockscreen = () => { const showOverLockscreen = () => {
let ctx = Utils.ad.getApplicationContext() let ctx = Utils.ad.getApplicationContext()
@ -103,12 +70,9 @@ const showOverLockscreen = () => {
} }
} }
const intentListener = ({ intent, android }: any) => { const intentListener = ({ intent, android }: any) => {
console.log(intent, android)
let action = ((intent || android).getStringExtra('action') || let action = ((intent || android).getStringExtra('action') ||
(android && android.getAction())) as string (android && android.getAction())) as string
if (action) { if (action) {
console.log(action)
switch (action) { switch (action) {
case 'new_recipe': case 'new_recipe':
renderView = EditRecipe renderView = EditRecipe
@ -118,30 +82,47 @@ const intentListener = ({ intent, android }: any) => {
break break
case 'timer': case 'timer':
renderView = CookingTimer renderView = CookingTimer
switch (ApplicationSettings.getNumber('isTimer', 0)) {
case 0:
// Closing all modals if available before navigation
let modals = Frame.topmost()._getRootModalViews()
for (let i = modals.length - 1; i >= 0; i--) {
Frame.topmost()
._getRootModalViews()
[i].closeModal()
}
Vue.navigateTo(CookingTimer as any, { Vue.navigateTo(CookingTimer as any, {
animated: false, animated: false,
}) })
ApplicationSettings.setNumber('isTimer', 1)
break break
case 'grocery': case 2:
renderView = GroceryList Vue.navigateBack()
break break
} }
break
// case 'grocery':
// renderView = GroceryList
// break
}
} }
} }
Application.on(Application.resumeEvent, (args) => { Application.on(Application.resumeEvent, () => {
console.log('App Resume')
showOverLockscreen() showOverLockscreen()
if (
utils.sysLocale() !==
ApplicationSettings.getString('sysLocale', utils.sysLocale())
) {
Frame.reloadPage()
utils.updateLocale()
}
}) })
Application.on(Application.launchEvent, (args) => { Application.on(Application.launchEvent, (args) => {
console.log('App Launch') utils.updateLocale()
console.log('RTL', store.state.RTL)
store.commit('setRTL') store.commit('setRTL')
if (args.android) {
androidLaunchEventLocalizationHandler()
intentListener(args) intentListener(args)
}
Application.android.on( Application.android.on(
AndroidApplication.activityNewIntentEvent, AndroidApplication.activityNewIntentEvent,
intentListener intentListener
@ -150,7 +131,6 @@ Application.on(Application.launchEvent, (args) => {
}) })
Application.on(Application.exitEvent, () => { Application.on(Application.exitEvent, () => {
console.log('App Exit')
renderView = EnRecipes renderView = EnRecipes
Application.android.off( Application.android.off(
AndroidApplication.activityNewIntentEvent, AndroidApplication.activityNewIntentEvent,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 938 B

After

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 780 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 852 B

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 906 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

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

After

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

Binary file not shown.

Before

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

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