Merge remote-tracking branch 'origin/main' into main

This commit is contained in:
Hosted Weblate 2021-06-15 19:02:55 +02:00
commit 4ca37dd0de
No known key found for this signature in database
GPG key ID: A3FAAA06E6569B4C
155 changed files with 6238 additions and 4078 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 ;)
- 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/">
<img src="https://hosted.weblate.org/widgets/enrecipes/-/app-translations/svg-badge.svg" alt="Translation status" /></a>
<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>
<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.
@ -28,16 +28,17 @@
- Scale your recipe ingredients to serve more or less people
- 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.
- Shake device to view random recipe
- Shake your device to view a random recipe
- Create meal plans
- Import/Export recipes
- Light, Dark and Black themes
- Set cooking timers
- You can Import or Export your data
- Has Light, Dark and Black themes
## Highlights
- 100% free and open-source
- Private by Design
- No permissions required
- No special permissions required
- No annoying ads or pop-ups
**Languages being translated**:

View file

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

View file

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

View file

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

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>
<Page @loaded="pgLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="*">
<GridLayout rows="*, auto, 72" columns="*">
<ScrollView
@scroll="!edit && svLoad($event)"
rowSpan="2"
@scroll="!edit && svScroll($event)"
rowSpan="3"
scrollBarIndicatorVisible="false"
>
<StackLayout>
<GridLayout rows="auto" columns="*, auto, 8">
<Label class="pageTitle" :text="'planner' | L" />
<Button
<RGridLayout :rtl="RTL" columns="*, auto, 12">
<Label class="pageTitle a" :text="'planner' | L" />
<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"
class="ico"
:text="icon.cog"
@tap="$navigateTo(MPSettings)"
:text="formattedDate(0)"
/>
<Button
class="ico min"
col="2"
:text="icon.right"
@tap="navigate(1)"
/>
</GridLayout>
<GridLayout
<RGridLayout
:rtl="RTL"
class="calendar"
columns="*, *, *, *, *, *, *"
rows="auto, auto, auto, auto, auto, auto, auto, auto"
:rows="calRows"
>
<Button class="ico navBtn" :text="icon.left" @tap="prevMonth" />
<Label
class="monthName"
@touch="touchMonthYearPicker"
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"
class="dayName sub rtl"
:class="{ f: RTL }"
:col="i"
v-for="(d, i) in getDayNames"
:key="d"
:key="d + i"
:text="d | L"
/>
<Button
class="min day"
:class="{
tb: isToday(d),
activeDay: isActive(d),
hasPlans: hasPlans(d),
}"
v-for="(cal, i) in getCal"
:key="i"
:row="getrow(i)"
:col="i % 7"
v-for="(d, i) in getCal"
:key="i"
:text="d ? d : null"
@tap="setToday(d)"
:class="dayClasses(cal)"
:text="cal.ld"
@tap="setDate(cal)"
/>
</GridLayout>
<StackLayout class="dayPlan">
<StackLayout
v-for="(mealType, index) in mealTimesWithRecipes"
:key="'mealType' + index"
>
<GridLayout columns="auto, auto">
<Label
class="periodLabel tb"
:class="mealType"
:text="mealType | L"
/>
<Button
:visibility="edit ? 'visible' : 'hidden'"
col="1"
class="ico"
:text="icon.plus"
@tap="addRecipe(mealType)"
/>
</GridLayout>
<GridLayout
:columns="`*, ${edit ? 'auto' : 0}`"
v-for="(recipeID, index) in getRecipes[mealType]"
:key="mealType + index"
</RGridLayout>
<StackLayout class="plans">
<RLabel
v-if="plannerView != 'd' && mealPlans.length"
class="date tb"
:text="formattedDate(1)"
textWrap="true"
/>
<StackLayout v-for="(meal, i) in mealTypes" :key="'meal' + i">
<Label
:hidden="!getRecipes[meal]"
class="meal tb"
:class="[meal]"
:text="meal | L"
/>
<RGridLayout
:rtl="RTL"
v-for="(plan, i) in getRecipes[meal]"
:key="meal + i"
class="plan"
columns="*, auto"
>
<Button
class="recipeTitle"
:text="getRecipeTitle(recipeID)"
@tap="viewRecipe(recipeID)"
<RGridLayout
:rtl="RTL"
class="rtl"
: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
:visibility="edit ? 'visible' : 'hidden'"
:hidden="!edit"
col="1"
class="ico x"
class="ico min"
:text="icon.x"
@tap="removeRecipe(mealType, recipeID)"
@tap="removeRecipe(plan.id)"
/>
</GridLayout>
</RGridLayout>
</StackLayout>
</StackLayout>
</StackLayout>
</ScrollView>
<GridLayout
row="1"
<GridLayout rowSpan="2" rows="*, auto" v-if="!mealPlans.length">
<StackLayout row="1" class="emptyState">
<RLabel class="title" :text="'ehwmp' | L" />
<RLabel :text="'plsCrt' | L" />
</StackLayout>
</GridLayout>
<RGridLayout
:rtl="RTL"
row="2"
@loaded="abLoad"
class="appbar"
: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
class="ico"
:text="icon.tod"
@ -115,13 +149,22 @@
col="2"
/>
<Button
class="ico fab"
:hidden="!hasRecipes"
class="ico"
:text="edit ? icon.done : icon.edit"
@tap="toggleEditMode"
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
row="2"
:hidden="!showUndo"
:count="countdown"
:msg="snackMsg"
@ -133,14 +176,21 @@
</Page>
</template>
<script>
import { Observable, CoreTypes } from "@nativescript/core";
<script lang="ts">
import { Frame, Observable, CoreTypes, Screen } from "@nativescript/core";
import { mapState, mapActions } from "vuex";
import ViewRecipe from "./ViewRecipe";
import MPSettings from "./settings/MPSettings";
import ActionWithSearch from "./modals/ActionWithSearch";
import MonthYearPicker from "./modals/MonthYearPicker";
import SnackBar from "./sub/SnackBar";
import ViewRecipe from "./ViewRecipe.vue";
import EditRecipe from "./EditRecipe.vue";
import EnRecipes from "./EnRecipes.vue";
import MPSettings from "./settings/MPSettings.vue";
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;
export default {
@ -149,10 +199,9 @@ export default {
},
data() {
return {
mealTimes: ["breakfast", "lunch", "dinner", "snacks"],
dNames: ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"],
mealTypes: ["breakfast", "lunch", "dinner", "snacks"],
year: 2021,
mNames: [
monthNames: [
"January",
"February",
"March",
@ -168,20 +217,27 @@ export default {
],
month: 0,
date: null,
edit: false,
edit: 0,
scrollPos: 1,
appbar: null,
snackbar: null,
countdown: 5,
snackMsg: null,
showUndo: false,
undo: false,
MPSettings: MPSettings,
showUndo: 0,
undo: 0,
temp: 0,
};
},
computed: {
...mapState(["icon", "recipes", "mealPlans", "mondayFirst"]),
...mapState([
"icon",
"recipes",
"layout",
"mealPlans",
"mondayFirst",
"RTL",
"plannerView",
]),
todaysTime() {
return new Date(this.year, this.month, this.date, 0).getTime();
},
@ -189,33 +245,58 @@ export default {
if (this.mealPlans.length) {
return this.mealPlans.reduce((acc, e) => {
if (e.date == this.todaysTime) {
acc[e.type] = [...(acc[e.type] || []), e.title];
acc[e.mealType] = [...(acc[e.mealType] || []), e];
}
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() {
let dNames = [...this.dNames];
if (!this.mondayFirst) dNames.unshift(dNames.pop());
let dNames =
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;
},
getCal() {
let y = this.year;
let m = this.month;
let ds = new Date(y, m + 1, 0).getDate();
let fd = new Date(y, m, 1).getDay();
let ld = new Date(y, m, ds).getDay();
if (this.mondayFirst) fd -= 1;
let days = new Array(fd).fill(0);
// let prevDays = Array.from(
// { length: fd },
// (e, k) => k + new Date(!m ? y - 1 : y, m, 0).getDate() - fd + 1
// );
// let days = prevDays;
for (let i = 1; i <= ds; i++) days.push(i);
// for (let i = 1; i <= 6 - ld; i++) days.push(i);
return days;
const getDays = (s, e) => {
let a = [];
for (
let d = new Date(s);
d <= new Date(e);
d.setDate(d.getDate() + 1)
) {
a.push({
d: d.getDate(),
ld: this.getLocaleN(d.getDate()),
m: d.getMonth(),
y: d.getFullYear(),
});
}
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() {
let d = new Date();
@ -225,22 +306,24 @@ export default {
this.date == d.getDate()
);
},
mealTimesWithRecipes() {
return this.mealTimes.filter(
(e) => (this.getRecipes[e] && this.getRecipes[e].length) || this.edit
);
hasRecipes() {
return this.mealTypes.filter(
(e) => this.getRecipes[e] && this.getRecipes[e].length
).length;
},
noImg() {
return /simple|minimal/.test(this.layout);
},
noAttr() {
return /minimal/.test(this.layout);
},
},
methods: {
...mapActions([
"setComponent",
"addMealPlanAction",
"deleteMealPlanAction",
]),
...mapActions(["addMealPlanAction", "deleteMealPlanAction"]),
pgLoad({ object }) {
object.bindingContext = new Observable();
this.setComponent("MealPlanner");
if (!this.date || this.date === new Date().getDate()) this.goToToday();
this.showBar();
},
abLoad({ object }) {
this.appbar = object;
@ -248,7 +331,7 @@ export default {
sbLoad({ object }) {
this.snackbar = object;
},
svLoad(args) {
svScroll(args) {
let scrollUp;
let y = args.scrollY;
if (y) {
@ -258,96 +341,143 @@ export default {
if (!scrollUp && ab == 0) {
this.appbar.animate({
translate: { x: 0, y: 64 },
duration: 250,
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
});
} else if (scrollUp && ab == 64) {
this.appbar.animate({
translate: { x: 0, y: 0 },
duration: 250,
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
});
}
}
},
// HELPERS
showAppBar() {
this.appbar.translateY = 0;
// Helpers
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) {
return Math.floor(2 + i / 7);
return Math.floor(1 + i / 7);
},
getDate(index) {
let date = new Date();
date.setDate(date.getDate() + index);
return date.getTime();
},
getRecipeImage(id) {
let r = this.recipes.filter((e) => e.id === id)[0];
return r && r.image;
},
getRecipeTitle(id) {
let recipe = this.recipes.filter((e) => e.id === id)[0];
return recipe ? recipe.title : `[ ${this.$options.filters.L("resNF")} ]`;
let r = this.recipes.filter((e) => e.id === id)[0];
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
viewRecipe(recipeID) {
let recipe = this.recipes.filter((e) => e.id === recipeID)[0];
if (recipe) {
// NavigationHandlers
viewRecipe(id) {
let mp = this.mealPlans.filter((e) => e.id == id)[0];
let r = this.recipes.filter((e) => e.id === mp.recipeID)[0];
if (r) {
this.$navigateTo(ViewRecipe, {
props: {
filterTrylater: true,
recipeID,
filterTrylater: 1,
recipeID: r.id,
yieldQuantity: mp.quantity,
},
});
}
},
// CALENDAR
prevMonth() {
if (this.month == 0) {
this.year--;
this.month = 11;
} else this.month--;
this.showAppBar();
navigateTo() {
this.$navigateTo(MPSettings, {
transition: {
name: this.RTL ? "slideRight" : "slide",
duration: 200,
curve: "easeOut",
},
});
},
nextMonth() {
if (this.month == 11) {
this.year++;
this.month = 0;
} else this.month++;
this.showAppBar();
navigateBack() {
Frame.topmost().backStack.length
? this.$navigateBack()
: 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() {
let d = new Date();
this.year = d.getFullYear();
this.month = d.getMonth();
this.date = d.getDate();
this.showAppBar();
},
isToday(date) {
let d = new Date();
return (
this.year == d.getFullYear() &&
this.month == d.getMonth() &&
date == d.getDate()
);
dayClasses({ d, m }) {
let classes = "min ";
let dt1 = new Date();
let dt2 = new Date(this.year, m, d, 0).getTime();
if (
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) {
return this.date == date;
},
hasPlans(date) {
let d = new Date(this.year, this.month, date, 0).getTime();
return this.mealPlans.filter((e) => e.date == d).length;
},
setToday(date) {
if (date) this.date = date;
setDate({ d, m, y }) {
this.year = y;
this.month = m;
this.date = d;
this.showBar();
},
toggleEditMode() {
this.edit = !this.edit;
},
openMonthYearPicker() {
this.$showModal(MonthYearPicker, {
this.$showModal(DMYPicker, {
props: {
title: "gtD",
monthNames: this.mNames,
monthNames: this.monthNames,
currentD: this.date,
currentM: this.month,
currentY: this.year,
},
@ -355,69 +485,140 @@ export default {
if (res) {
this.month = res.month;
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
newMealPlan({ date, type, title, index, inDB }) {
// DataHandlers
newMealPlan({ plan, index, inDB }) {
this.addMealPlanAction({
date: date ? date : this.todaysTime,
type,
title,
plan,
index,
inDB,
});
},
addRecipe(type) {
let filteredRecipes = this.recipes.filter((e) =>
this.getRecipes[type] ? !this.getRecipes[type].includes(e.id) : true
);
this.$showModal(ActionWithSearch, {
addMealPlan() {
this.$showModal(Action, {
props: {
title: "selRec",
recipes: filteredRecipes,
title: "add",
list: ["rec", "no"],
},
}).then(
(title) =>
title &&
this.newMealPlan({ date: 0, type, title, index: null, inDB: true })
);
}).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, {
props: {
title: "selRec",
recipes,
action: "aNBtn",
},
}).then((res) => {
if (res == "aNBtn") {
this.$navigateTo(EditRecipe, {
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() {
if (this.temp) {
this.temp.inDB = 1;
this.deleteMealPlanAction(this.temp);
let { plan, index } = this.temp;
this.deleteMealPlanAction({ id: plan.id, index, inDB: 1 });
this.temp = 0;
}
},
removeRecipe(type, title) {
removeRecipe(id) {
this.deleteTempFromDB();
let date = this.todaysTime;
let index = this.mealPlans.findIndex(
(e) => e.date == date && e.type == type && e.title == title
);
let mealPlan = {
date,
type,
title,
index,
};
let temp;
this.temp = temp = mealPlan;
this.deleteMealPlanAction(mealPlan);
this.showUndoBar("recRm")
.then(() => this.newMealPlan({ date, type, title, index }))
let index = this.mealPlans.findIndex((e) => e.id == id);
let plan = this.mealPlans.filter((e) => e.id == id)[0];
this.temp = { plan, index };
this.deleteMealPlanAction({ id, index });
this.showUndoBar(plan.note ? "rmN" : "recRm")
.then(() => this.newMealPlan({ plan, index }))
.catch(() => {
temp.inDB = 1;
console.log("deleting inDB after catch: ", temp);
this.deleteMealPlanAction(temp);
this.deleteMealPlanAction({ id, index, inDB: 1 });
});
},
showUndoBar(message) {
return new Promise((resolve, reject) => {
this.animateBar(this.appbar, 0).then(() => {
this.showUndo = true;
this.showUndo = 1;
this.snackMsg = message;
this.countdown = 5;
this.animateBar(this.snackbar, 1).then(() => {
@ -426,12 +627,12 @@ export default {
barTimer = setInterval(() => {
if (this.undo) {
this.hideBar();
resolve(true);
resolve(1);
}
this.countdown = Math.round((a -= 0.1));
if (this.countdown < 1) {
this.hideBar();
reject(true);
reject(1);
}
}, 100);
});
@ -440,22 +641,76 @@ export default {
},
hideBar() {
clearInterval(barTimer);
this.deleteTempFromDB();
this.animateBar(this.snackbar, 0).then(() => {
this.showUndo = this.undo = false;
this.showUndo = this.undo = 0;
this.animateBar(this.appbar, 1);
});
},
undoDel() {
this.undo = true;
this.undo = 1;
},
//HELPERS
touchMonthYearPicker({ object, action }) {
object.className = action.match(/down|move/)
? "monthName fade"
: "monthName";
// Helpers
formattedDate(v) {
let d = new Date(this.year, this.month, this.date, 0, 0, 0);
let today = new Date();
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();
},
touchRecipe({ object, action }) {
object.className = action.match(/down|move/) ? "fade" : "";
},
},
};
</script>

View file

@ -1,16 +1,21 @@
<template>
<Page @loaded="onPageLoad" @unloaded="onPageUnload" actionBarHidden="true">
<GridLayout rows="*, auto" columns="*">
<DockLayout stretchLastChild="true" rowSpan="2">
<GridLayout
<Page @loaded="pgLoad" @unloaded="onPageUnload" actionBarHidden="true">
<GridLayout rows="*, auto, auto" columns="auto, *, auto">
<DockLayout stretchLastChild="true" rowSpan="3" colSpan="3">
<RGridLayout
:rtl="RTL"
dock="top"
rows="auto,auto"
columns="*, auto"
paddingBottom="24"
>
<StackLayout>
<Label class="pageTitle" paddingBottom="8" :text="recipe.title" />
<StackLayout marginLeft="12" orientation="horizontal">
<RLabel class="pageTitle" paddingBottom="8" :text="recipe.title" />
<StackLayout
:class="{ f: RTL }"
margin="0 12"
orientation="horizontal"
>
<Button
class="ico rate"
:class="{ rated: recipe.rating >= n }"
@ -34,72 +39,82 @@
decodeHeight="96"
@tap="viewPhoto"
/>
</GridLayout>
</RGridLayout>
<AbsoluteLayout dock="bottom">
<ScrollView
dock="bottom"
width="100%"
height="100%"
@loaded="onScrollLoad"
@scroll="onScroll($event)"
@scroll="svScroll($event)"
>
<StackLayout>
<GridLayout rows="auto" columns="*, *">
<RGridLayout :rtl="RTL" rows="auto" columns="*, *">
<StackLayout class="attribute">
<Label class="title sub" :text="'cui' | L" />
<Label class="value" :text="recipe.cuisine | L" />
<RLabel class="sub" :text="'cui' | L" />
<RLabel class="value" :text="recipe.cuisine | L" />
</StackLayout>
<StackLayout class="attribute" col="1">
<Label class="title sub" :text="'cat' | L" />
<Label class="value" :text="recipe.category | L" />
<RLabel class="sub" :text="'cat' | L" />
<RLabel class="value" :text="recipe.category | L" />
</StackLayout>
</GridLayout>
<StackLayout :hidden="!recipe.tags.length" class="attribute">
<Label class="title sub" :text="'ts' | L" />
<Label class="value" :text="getTags(recipe.tags)" />
</RGridLayout>
<StackLayout
:hidden="!recipe.tags.length"
class="attribute hal"
:class="{ r: RTL }"
>
<RLabel class="sub" :text="'ts' | L" />
<RLabel class="value" :text="getTags(recipe.tags)" />
</StackLayout>
<GridLayout rows="auto" columns="*, *">
<RGridLayout :rtl="RTL" rows="auto" columns="*, *">
<StackLayout
class="attribute"
:hidden="!hasTime(recipe.prepTime)"
>
<Label class="title sub" :text="'prepT' | L" />
<Label class="value" :text="formattedTime(recipe.prepTime)" />
<RLabel class="sub" :text="'prepT' | L" />
<RLabel
class="value"
:text="formattedTime(recipe.prepTime)"
/>
</StackLayout>
<StackLayout
:col="hasTime(recipe.prepTime) ? 1 : 0"
class="attribute"
:hidden="!hasTime(recipe.cookTime)"
>
<Label class="title sub" :text="'cookT' | L" />
<Label class="value" :text="formattedTime(recipe.cookTime)" />
<RLabel class="title sub" :text="'cookT' | L" />
<RLabel
class="value"
:text="formattedTime(recipe.cookTime)"
/>
</StackLayout>
</GridLayout>
<GridLayout rows="auto" columns="*, *">
</RGridLayout>
<RGridLayout :rtl="RTL" rows="auto" columns="*, *">
<StackLayout class="attribute">
<Label class="title sub" :text="'yld' | L" />
<Label
<RLabel class="title sub" :text="'yld' | L" />
<RLabel
@touch="touchYield"
class="value clickable"
horizontalAlignment="left"
class="value accent"
:text="`${tempYieldQuantity} ${$options.filters.L(
recipe.yieldUnit
)}`"
/>
</StackLayout>
<StackLayout class="attribute" col="1">
<Label class="title sub" :text="'Difficulty level' | L" />
<Label class="value" :text="recipe.difficulty | L" />
<RLabel class="title sub" :text="'Difficulty level' | L" />
<RLabel class="value" :text="recipe.difficulty | L" />
</StackLayout>
</GridLayout>
</RGridLayout>
<StackLayout @loaded="onIngsLoad">
<Label
<RLabel
padding="0 16"
class="sectionTitle"
:hidden="!recipe.ingredients.length"
:text="getTitleCount('ings', 'ingredients')"
/>
<StackLayout
<RStackLayout
:rtl="RTL"
orientation="horizontal"
v-for="(item, index) in recipe.ingredients"
:key="index + 'ing'"
@ -107,39 +122,29 @@
@touch="touchIngredient($event, index)"
>
<Button class="ico min" :text="icon.uncheck" />
<Label
class="value tw"
:text="`${
roundedQuantity(item.quantity)
? roundedQuantity(item.quantity) + ' '
: ''
}${
roundedQuantity(item.quantity)
? $options.filters.L(item.unit) + ' '
: ''
}${item.item}`"
/>
</StackLayout>
<RLabel class="value tw" :text="getIngredientItem(item)" />
</RStackLayout>
</StackLayout>
<StackLayout @loaded="onInsLoad">
<Label
<RLabel
padding="0 16"
:hidden="!recipe.instructions.length"
class="sectionTitle"
:text="getTitleCount('inss', 'instructions')"
/>
<StackLayout
<RStackLayout
:rtl="RTL"
orientation="horizontal"
@touch="touchInstruction"
v-for="(instruction, index) in recipe.instructions"
:key="index + 'ins'"
class="instruction"
>
<Button class="count ico min" :text="index + 1" />
<Label class="value tw" :text="instruction" />
</StackLayout>
<Button class="count ico min" :text="getLocaleN(index + 1)" />
<RLabel class="value tw" :text="instruction" />
</RStackLayout>
</StackLayout>
<Label
<RLabel
@loaded="onCmbLoad"
padding="0 16"
:hidden="!recipe.combinations.length"
@ -153,7 +158,7 @@
:text="getCombinationTitle(combination)"
@tap="viewCombination(combination)"
/>
<Label
<RLabel
@loaded="onNosTLoad"
padding="0 16"
:hidden="!recipe.notes.length"
@ -161,17 +166,10 @@
:text="getTitleCount('nos', 'notes')"
/>
<StackLayout @loaded="onNosLoad" padding="0 16"> </StackLayout>
<Label
class="dateInfo sub tw"
:text="`${$options.filters.L('Last updated')}: ${formattedDate(
recipe.lastModified
)}\n${$options.filters.L('Created')}: ${formattedDate(
recipe.created
)}`"
/>
<Label class="dateInfo sub tw" :text="getDates().uc" />
</StackLayout>
</ScrollView>
<Label
<RLabel
@loaded="onStickyLoad"
class="sectionTitle sticky"
:hidden="!stickyTitle"
@ -181,61 +179,85 @@
</DockLayout>
<GridLayout
row="1"
@loaded="onAppBarLoad"
class="appbar"
v-show="!toast"
columns="auto, *, auto, auto, auto, auto, auto"
@loaded="sbload"
class="appbar sidebar"
:col="RTL ? 0 : 2"
rows="auto, auto, auto"
>
<Button class="ico" :text="icon.timer" @tap="openCookingTimer" />
<Button
row="1"
:hidden="busyDup"
class="ico"
:text="photoOpen ? icon.x : icon.back"
@tap="navigateBack"
:class="{ f: RTL }"
: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
col="2"
class="ico"
:text="icon.timer"
@tap="openCookingTimer"
/>
<Button
col="3"
v-if="!filterTrylater"
class="ico"
:text="recipe.tried ? icon.try : icon.tried"
@tap="toggle('tried')"
/>
<Button
col="3"
col="2"
v-else
class="ico"
:text="icon.done"
@tap="toggle('tried', true)"
@tap="toggle('tried', 1)"
/>
<Button
col="4"
col="3"
class="ico"
:text="recipe.favorite ? icon.faved : icon.fav"
@tap="toggle('favorite')"
/>
<Button
col="5"
v-if="!busy"
col="4"
:hidden="busyEdit"
class="ico"
:text="icon.edit"
@tap="editRecipe"
/>
<ActivityIndicator col="5" v-else :busy="busy" />
<ActivityIndicator col="4" :hidden="!busyEdit" :busy="busyEdit" />
<Button
col="6"
col="5"
class="ico fab"
:text="icon.share"
@tap="shareHandler"
/>
</GridLayout>
<Toast :toast="toast" :action="hideLastTried" />
<AbsoluteLayout rowSpan="2">
</RGridLayout>
<Toast
row="2"
colSpan="3"
:onload="tbLoad"
:toast="toast"
:action="hideBar"
/>
<AbsoluteLayout rowSpan="3" colSpan="3">
<Image
@tap="({ object }) => (object.cancel = true)"
@tap="closePhoto"
backgroundColor="black"
stretch="aspectFit"
@loaded="onImgViewLoad"
@ -243,6 +265,7 @@
class="photoviewer"
/>
</AbsoluteLayout>
<WebView @loaded="wvLoad" hidden />
</GridLayout>
</Page>
</template>
@ -255,13 +278,13 @@ import {
Utils,
Span,
FormattedString,
Label,
Observable,
Screen,
CoreTypes,
WebView,
} from "@nativescript/core";
import { RLabel } from "~/rtl-ui";
import { localize } from "@nativescript/localize";
const intl = require("nativescript-intl");
import { mapActions, mapState } from "vuex";
import CookingTimer from "./CookingTimer.vue";
import EditRecipe from "./EditRecipe.vue";
@ -269,17 +292,24 @@ import Action from "./modals/Action.vue";
import Toast from "./sub/Toast.vue";
import Prompt from "./modals/Prompt.vue";
import * as utils from "~/shared/utils";
const Intl = require("nativescript-intl");
let barTimer;
declare const android: any;
export default {
components: { Toast },
props: ["filterTrylater", "recipeID"],
props: ["filterTrylater", "recipeID", "yieldQuantity"],
data() {
return {
busy: false,
busyEdit: 0,
busyDup: 0,
yieldMultiplier: 1,
recipe: null,
currentRecipeID: this.recipeID,
scrollview: null,
appbar: null,
sidebar: null,
toastbar: null,
ingcon: null,
inscon: null,
cmbcon: null,
@ -290,13 +320,15 @@ export default {
checked: 0,
stepsDid: 0,
toast: null,
photoOpen: false,
showTitleArr: [false, false, false, false],
photoOpen: 0,
showTitleArr: [0, 0, 0, 0],
sticky: null,
view: null,
wv: null,
};
},
computed: {
...mapState(["icon", "recipes"]),
...mapState(["icon", "recipes", "RTL"]),
tempYieldQuantity() {
return Math.abs(this.yieldMultiplier) > 0
? Math.abs(parseFloat(this.yieldMultiplier))
@ -320,29 +352,38 @@ export default {
}
return val;
},
hasPrinterSupport() {
return utils.Printer.isSupported();
},
},
methods: {
...mapActions([
"toggleStateAction",
"setComponent",
"setRatingAction",
"toggleCartAction",
]),
onPageLoad({ object }) {
...mapActions(["toggleStateAction", "setRatingAction", "toggleCartAction"]),
pgLoad({ object }) {
this.busyDup = this.busyEdit = this.photoOpen = 0;
object.bindingContext = new Observable();
this.busy = this.photoOpen = false;
this.setComponent("ViewRecipe");
if (this.yieldMultiplier == this.recipe.yieldQuantity)
this.yieldMultiplier = this.recipe.yieldQuantity;
this.keepScreenOn(true);
utils.keepScreenOn(1);
this.syncCombinations();
this.view = object.page.getViewById("printview");
},
onPageUnload() {
this.keepScreenOn(false);
utils.keepScreenOn(0);
},
onAppBarLoad({ object }) {
sbload({ object }) {
this.sidebar = object;
},
abLoad({ 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 }) {
this.ingcon = object;
},
@ -366,24 +407,22 @@ export default {
this.imgView = object;
this.imgView.visibility = "collapsed";
this.imgView.top = 24;
this.imgView.left = Screen.mainScreen.widthDIPs - 112;
this.imgView.left = this.RTL ? 16 : Screen.mainScreen.widthDIPs - 112;
},
onStickyLoad({ object }) {
this.sticky = object;
},
fixTitle({ object }, swipeUp: boolean): void {
fixTitle(object, swipeUp: boolean): void {
let ingL = this.recipe.ingredients.length;
let insL = this.recipe.instructions.length;
let cmbL = this.recipe.combinations.length;
let notL = this.recipe.notes.length;
const isTop = (label): boolean => {
let pos = label.getLocationRelativeTo(object).y;
return label === this.cmbcon || label === this.notesT
? pos < 0
: pos + 32 < 0;
};
const setVisibleTitle = (n: number): void => {
let arr = [ingL, insL, cmbL, notL];
this.showTitleArr = Array.from(
@ -391,7 +430,6 @@ export default {
(v, i) => (v = arr[i] ? i < n : false)
);
};
if (swipeUp) {
if (ingL && !this.showTitleArr[0] && isTop(this.ingcon))
setVisibleTitle(1);
@ -412,45 +450,71 @@ export default {
setVisibleTitle(0);
}
},
onScroll(args) {
svScroll({ object, scrollY }) {
let swipeUp: boolean;
let y = args.scrollY;
let y = scrollY;
if (y) {
swipeUp = y > this.scrollPos;
this.scrollPos = Math.abs(y);
this.fixTitle(args, swipeUp);
this.fixTitle(object, swipeUp);
if (!this.toast) {
let ab = this.appbar.translateY;
if (swipeUp && ab == 0)
this.appbar.animate({
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,
});
if (swipeUp && ab == 0) this.hideBars();
else if (!swipeUp && ab == 64) this.showBars();
}
}
},
// 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) {
let count = this.recipe[type].length;
let selected = null;
let c = this.recipe[type].length;
let s = null;
switch (title) {
case "ings":
selected = this.checked;
s = this.checked;
break;
case "inss":
selected = this.stepsDid;
s = this.stepsDid;
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;
},
getIngredientItem(item) {
return `${
this.roundedQuantity(item.quantity)
? this.roundedQuantity(item.quantity) + " "
: ""
}${this.roundedQuantity(item.quantity) ? localize(item.unit) + " " : ""}${
item.item
}`;
},
changeYield() {
this.$showModal(Prompt, {
props: {
@ -485,31 +549,25 @@ export default {
);
},
showLastTried() {
this.toast = localize("triedInfo", this.niceDate(this.recipe.lastTried));
utils.timer(10, (val) => {
if (!val) this.toast = val;
this.animateBar(this.appbar, 0).then(() => {
this.toast = localize(
"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 }) {
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;
this.toast = null;
});
hideBar() {
clearInterval(barTimer);
this.animateBar(this.toastbar, 0).then(() => {
this.toast = null;
this.photoOpen
? (this.appbar.opacity = 1)
: this.animateBar(this.appbar, 1);
});
},
// getMeasure(value: number, unit: string) {
// let vm = this;
@ -560,14 +618,6 @@ export default {
) / 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) {
let t = time.split(":");
let h = parseInt(t[0]);
@ -577,15 +627,13 @@ export default {
return h ? (m ? `${h} ${hr} ${m} ${min}` : `${h} ${hr}`) : `${m} ${min}`;
},
formattedDate(date) {
let d = new Date(date);
var dateFormat = new intl.DateTimeFormat(null, {
return new Intl.DateTimeFormat(null, {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
}).format(d);
return `${dateFormat}`;
}).format(new Date(date));
},
isValidURL(string) {
let pattern = new RegExp("^https?|^www", "ig");
@ -654,26 +702,38 @@ export default {
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() {
this.busy = true;
this.busyEdit = 1;
this.$navigateTo(EditRecipe, {
props: {
navigationFromView: true,
filterTrylater: this.filterTrylater,
recipeID: this.currentRecipeID,
},
// backstackVisible: false,
animated: false,
});
},
viewCombination(combination) {
this.scrollview.scrollToVerticalOffset(0, true);
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.clearSteps();
this.recipe.ingredients.forEach(() => this.checks.push(false));
this.recipe.ingredients.forEach(() => this.checks.push(0));
this.currentRecipeID = combination;
this.syncCombinations();
this.createNotes();
@ -681,7 +741,7 @@ export default {
this.recipe.tried && this.recipe.lastTried && this.showLastTried();
},
// SHARE ACTION
// ShareAction
shareHandler() {
if (this.recipe.image) {
this.$showModal(Action, {
@ -696,7 +756,7 @@ export default {
break;
case "pht":
ImageSource.fromFile(this.recipe.image).then((res) =>
utils.shareImage(res, localize("srpu"))
utils.shareImage(res, localize("srpu"), this.recipe.title)
);
break;
}
@ -769,7 +829,7 @@ export default {
utils.shareText(shareContent, localize("sru"));
},
// DATA HANDLERS
// DataHandlers
toggle(key: string, setDate: boolean) {
this.toggleStateAction({
id: this.currentRecipeID,
@ -787,7 +847,7 @@ export default {
}
},
// SHOPPINGLIST
// ShoppingList
toggleCart() {
if (!this.recipe.inBag) {
} else {
@ -797,10 +857,10 @@ export default {
});
},
// NOTES
// Notes
createNote(note) {
let regex = /(https?:\/\/[^\s]+)/g;
const lbl = new Label();
const lbl = new RLabel();
lbl.className = "note";
lbl.textWrap = true;
let fString = new FormattedString();
@ -851,7 +911,8 @@ export default {
} else this.$navigateBack();
},
viewPhoto() {
this.photoOpen = true;
this.hideBars();
this.photoOpen = 1;
this.hijackBackEvent();
let pv = this.imgView;
pv.visibility = "visible";
@ -865,16 +926,16 @@ export default {
pv.animate({
width: sw,
height: sw,
translate: { x: 112 - sw, y: (sh - sw) / 3 },
duration: 250,
translate: { x: this.RTL ? -16 : 112 - sw, y: (sh - sw) / 3 },
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
})
)
.then(() =>
pv.animate({
height: sh,
translate: { x: -sw + 112, y: -((sh - sw) / 6) },
duration: 250,
translate: { x: this.RTL ? -16 : 112 - sw, y: -((sh - sw) / 6) },
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
})
);
@ -886,8 +947,8 @@ export default {
pv.animate({
width: sw,
height: sw,
translate: { x: 112 - sw, y: (sh - sw) / 3 },
duration: 250,
translate: { x: this.RTL ? -16 : 112 - sw, y: (sh - sw) / 3 },
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
})
.then(() =>
@ -895,7 +956,7 @@ export default {
width: 96,
height: 96,
translate: { x: 0, y: 0 },
duration: 250,
duration: 200,
curve: CoreTypes.AnimationCurve.ease,
})
)
@ -907,37 +968,162 @@ export default {
)
.then(() => {
pv.visibility = "collapsed";
this.photoOpen = false;
this.photoOpen = 0;
this.releaseBackEvent();
this.showBars();
});
},
navigateBack() {
this.photoOpen ? this.closePhoto() : this.$navigateBack();
},
//TIMERS
// Timers
openCookingTimer() {
this.$navigateTo(CookingTimer, {
props: {
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 }) {
object.className = action.match(/down|move/)
? "value clickable fade"
: "value clickable";
? "value accent fade"
: "value accent";
if (action == "up") this.changeYield();
},
},
created() {
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() {
this.yieldMultiplier = this.recipe.yieldQuantity;
this.recipe.tried && this.recipe.lastTried && this.showLastTried();
this.yieldMultiplier = this.yieldQuantity || this.recipe.yieldQuantity;
},
};
</script>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,20 +2,25 @@
<Page
@loaded="transparentPage"
backgroundColor="transparent"
:class="appTheme"
:class="theme"
>
<GridLayout rows="auto, auto, auto" class="modal">
<Label class="title" :text="title | L" />
<RLabel class="title" :text="title | L" />
<StackLayout row="1">
<StackLayout class="input">
<TextField
@loaded="setGravity"
class="modalInput"
v-model="setLabel"
:hint="label"
autocapitalizationType="words"
autocorrect="true"
/></StackLayout>
<!-- @loaded="focusField" -->
<StackLayout orientation="horizontal" horizontalAlignment="center">
<RStackLayout
:rtl="RTL"
orientation="horizontal"
horizontalAlignment="center"
>
<ListPicker
@loaded="onLPLoad"
:items="hrsList"
@ -31,10 +36,15 @@
:items="secsList"
@selectedIndexChange="setSec"
></ListPicker>
</StackLayout>
</RStackLayout>
</StackLayout>
<GridLayout row="2" columns="auto, *, auto, auto" class="actions">
<RGridLayout
:rtl="RTL"
row="2"
columns="auto, *, auto, auto"
class="actions r"
>
<Button
v-if="showPreset"
class="text sm"
@ -45,10 +55,10 @@
col="2"
class="text sm"
:text="'cBtn' | L"
@tap="$modal.close(false)"
@tap="$modal.close(0)"
/>
<Button col="3" class="text sm" :text="action | L" @tap="sendRespose" />
</GridLayout>
</RGridLayout>
</GridLayout>
</Page>
</template>
@ -71,7 +81,7 @@ export default {
};
},
computed: {
...mapState(["icon", "appTheme"]),
...mapState(["icon", "theme", "RTL"]),
hrsList() {
let h = [...Array(24).keys()];
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>
<Page @loaded="onPageLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *">
<ListView
rowSpan="2"
colSpan="2"
class="options-list"
for="item in items"
>
<Page @loaded="pgLoad" actionBarHidden="true">
<RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<ListView rowSpan="2" colSpan="2" class="options" for="item in items">
<v-template if="$index == 0">
<Label class="pageTitle" :text="'About' | L" />
</v-template>
@ -23,20 +18,26 @@
<StackLayout class="listSpace"> </StackLayout>
</v-template>
<v-template>
<GridLayout
<RGridLayout
:rtl="RTL"
columns="auto, *"
class="option"
@touch="touch($event, item.url)"
>
<Label class="ico" :text="icon[item.icon]" />
<Label col="1" :text="item.title | L" />
</GridLayout>
<Label
class="ico"
:class="{ rtl: /help|don/.test(item.icon) }"
:text="icon[item.icon]"
/>
<Label col="1" class="info" :text="item.title | L" />
</RGridLayout>
</v-template>
</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()" />
</GridLayout>
</GridLayout>
</RGridLayout>
</Page>
</template>
@ -46,7 +47,7 @@ import { mapState } from "vuex";
export default {
computed: {
...mapState(["icon"]),
...mapState(["icon", "RTL"]),
items() {
return [
{},
@ -69,8 +70,7 @@ export default {
{
icon: "priv",
title: "priv",
url:
"https://github.com/vishnuraghavb/EnRecipes/blob/main/PRIVACY.md",
url: "https://github.com/vishnuraghavb/EnRecipes/blob/main/PRIVACY.md",
},
{
icon: "don",
@ -95,7 +95,7 @@ export default {
},
},
methods: {
onPageLoad({ object }) {
pgLoad({ object }) {
object.bindingContext = new Observable();
},
// HELPERS

View file

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

View file

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

View file

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

View file

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

View file

@ -1,44 +1,80 @@
<template>
<Page @loaded="onPageLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *">
<Page @loaded="pgLoad" actionBarHidden="true">
<RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<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()" />
</GridLayout>
</GridLayout>
</RGridLayout>
</Page>
</template>
<script>
import { Observable } from "@nativescript/core";
import { mapState, mapActions } from "vuex";
import Action from "../modals/Action";
import OptionsList from "../sub/OptionsList";
import { localize } from "@nativescript/localize";
export default {
components: { OptionsList },
computed: {
...mapState(["icon", "mondayFirst"]),
...mapState(["icon", "mondayFirst", "RTL", "plannerView", "planDeletion"]),
items() {
return [
{},
{
type: "list",
icon: "calv",
title: "calVM",
subTitle: localize(this.plannerView),
action: this.selectPlannerView,
},
{
type: "switch",
icon: "week",
title: "swm",
checked: this.mondayFirst,
checked: !!this.mondayFirst,
action: this.toggleFirstDay,
},
{
type: "list",
icon: "mpd",
title: "admp",
subTitle: localize(this.planDeletion),
action: this.selectDeletionTime,
},
{},
];
},
},
methods: {
...mapActions(["setFirstDay"]),
onPageLoad({ object }) {
...mapActions(["setFirstDay", "setPlannerView", "setPlanDeletion"]),
pgLoad({ object }) {
object.bindingContext = new Observable();
},
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>
<Page @loaded="onPageLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="auto, *">
<Page @loaded="pgLoad" actionBarHidden="true">
<RGridLayout :rtl="RTL" rows="*, auto" columns="auto, *">
<OptionsList title="opts" :items="items" />
<GridLayout
v-show="!toast"
:hidden="toast"
@loaded="abLoad"
row="1"
class="appbar"
class="appbar rtl"
rows="*"
columns="auto, *"
>
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
</GridLayout>
<Toast :toast="toast" :action="(toast = null)" />
</GridLayout>
<Toast :onload="tbLoad" :toast="toast" :action="hideBar" />
</RGridLayout>
</Page>
</template>
@ -28,11 +29,13 @@ export default {
components: { OptionsList, Toast },
data() {
return {
appbar: null,
toastbar: null,
toast: null,
};
},
computed: {
...mapState(["icon", "shakeEnabled"]),
...mapState(["icon", "shake", "RTL"]),
items() {
return [
{},
@ -40,8 +43,8 @@ export default {
type: "switch",
icon: "shuf",
title: "sVw",
subTitle: "sVwInfo",
checked: this.shakeEnabled,
subTitle: localize("sVwInfo"),
checked: !!this.shake,
action: this.toggleShake,
},
{},
@ -50,19 +53,35 @@ export default {
},
methods: {
...mapActions(["setShake"]),
onPageLoad({ object }) {
pgLoad({ object }) {
object.bindingContext = new Observable();
},
abLoad({ object }) {
this.appbar = object;
},
tbLoad({ object }) {
this.toastbar = object;
},
// SHAKE VIEW RANDOM RECIPE
toggleShake() {
let checked = this.shakeEnabled;
if (checked && !utils.hasAccelerometer()) {
this.toast = localize("noAccSensor");
utils.timer(5, (val) => {
if (!val) this.toast = val;
});
} else this.setShake(!checked);
let checked = this.shake;
if (checked && !utils.hasAccelerometer())
this.showToast(localize("noAccSensor"));
else this.setShake(!checked | 0);
},
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);
});
},
},
};

View file

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

View file

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

View file

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

View file

@ -1,193 +1,280 @@
<template>
<GridLayout
<RGridLayout
:rtl="RTL"
rows="auto, auto"
columns="auto, *, auto, auto, auto"
class="singleTimer"
class="timer"
>
<!-- :class="{ blink: done }" -->
<Button
class="ico"
class="ico min rtl"
:text="done ? icon.ring : timer.isPaused ? icon.start : icon.pause"
@tap="!done && toggleProgress()"
/>
<StackLayout col="1" class="info" :colSpan="timer.isPaused ? 1 : 2">
<Label :text="timer.label" class="tb title tw" />
<Label
@touch="!timer.recipeID && touch($event)"
<StackLayout
col="1"
class="info"
: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"
class="recipeTitle"
:class="timer.recipeID ? 'sub' : 'clickable'"
class="a"
:class="timer.recipeID ? 'sub' : 'accent'"
/>
<RLabel
:text="
progress == 0
? countUp
? getCount
: formattedTime(timer.time)
: getCount
"
/>
<Label :text="formattedTime(timer.time)" />
</StackLayout>
<Button
col="2"
class="ico"
:hidden="(!timer.isPaused || progress == 0) && !done"
:text="icon.reset"
@tap="resetTimer"
class="ico rtl"
:hidden="(!timer.isPaused && timer.preset) || done || countUp"
:text="isReset || timer.preset ? icon.reset : icon.addTo"
@tap="isReset || timer.preset ? resetTimer() : addPreset()"
/>
<Button
col="3"
class="ico"
:hidden="timer.preset && !done"
@tap="done ? delay() : addPreset()"
:text="done ? icon.delay : icon.addTo"
@tap="countUp ? addPreset() : addDelay()"
:text="countUp ? icon.addTo : icon.delay"
/>
<Button
col="4"
class="ico x"
class="ico min"
:text="icon.x"
@tap="removeTimerItem(timerIndex, false)"
@tap="removeTimer(timer.id, done)"
/>
<Progress row="1" colSpan="5" :value="progress" />
</GridLayout>
<Progress @loaded="pLoaded" row="1" colSpan="5" />
</RGridLayout>
</template>
<script>
import { ApplicationSettings, Application } from "@nativescript/core";
import { ApplicationSettings } from "@nativescript/core";
import { localize } from "@nativescript/localize";
import { mapState, mapActions } from "vuex";
import ActionWithSearch from "../modals/ActionWithSearch";
import ViewRecipe from "../ViewRecipe";
import * as utils from "~/shared/utils";
import { EventBus } from "~/main";
import { EvtBus } from "~/main";
export default {
props: [
"timer",
"timerIndex",
"formattedTime",
"removeTimer",
"addToPreset",
"togglePause",
"fireTimer",
"timerAlert",
"showToast",
],
data() {
return {
pBar: 0,
count: 0,
delay: 0,
progress: 0,
timerInt: this.timer.timerInterval,
};
},
computed: {
...mapState(["icon", "recipes", "timerDelay"]),
...mapState(["icon", "recipes", "timerDelay", "timerPresets", "RTL"]),
getRecipeTitle() {
let { recipeID } = this.timer;
if (recipeID) {
let recipe = this.recipes.filter(
(e) => e.id === this.timer.recipeID
)[0];
return recipe
? recipe.title
: `[ ${this.$options.filters.L("resNF")} ]`;
let recipe = this.recipes.filter((e) => e.id == this.timer.recipeID)[0];
if (recipe) return recipe.title;
else {
this.timer.recipeID = null;
return localize("fwr");
}
} else return localize("fwr");
},
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(":");
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: {
...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() {
this.$showModal(ActionWithSearch, {
props: {
title: "selRec",
recipes: this.recipes,
action: "aNBtn",
},
}).then((recipeID) => {
let timer = this.timer;
timer.recipeID = recipeID;
this.updateActiveTimer(timer);
}).then((res) => {
if (res == "aNBtn") {
this.$navigateTo(EditRecipe, {
animated: false,
});
} else if (res) {
let timer = this.timer;
timer.recipeID = res;
this.sortActiveTimers();
}
});
},
setProgress(progress, delay) {
this.progress = progress;
let timer = this.timer;
if (progress <= 100 && !timer.timerInterval) {
timer.timerInterval = setInterval(() => {
setNum(type, val) {
ApplicationSettings.setNumber(`${this.timer.id}${type}`, val);
},
setProgress() {
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) {
this.progress += 100 / (delay + this.getTimeInSec);
ApplicationSettings.setNumber(
`${this.timer.id}progress`,
this.progress
);
}
if (this.progress >= 100) {
if (progress < 100) this.fireTimer(timer);
clearInterval(timer.timerInterval);
ApplicationSettings.remove(`${this.timer.id}delay`);
this.setNum("c", this.countUp ? this.count++ : --this.count);
this.setProgress();
} else this.resetInterval();
if (this.progress >= 100 && this.count >= 0) {
this.timer.done = 1;
this.timerAlert();
}
}, 1000);
this.updateActiveTimer(timer);
}
},
resetInterval() {
let timer = this.timer;
clearInterval(this.timer.timerInterval);
timer.timerInterval = null;
this.updateActiveTimer(timer);
clearInterval(this.timer.timerInt);
this.timer.timerInt = 0;
this.pBar.setIndeterminate(false);
},
resetTimer() {
this.resetInterval();
this.togglePause(this.timer, true);
ApplicationSettings.remove(`${this.timer.id}delay`);
this.setProgress(0, 0);
ApplicationSettings.setNumber(`${this.timer.id}progress`, 0);
this.clearNotification();
this.count = this.actualTime;
this.progress = this.delay = this.timer.done = 0;
ApplicationSettings.remove(`${this.timer.id}d`);
this.setNum("c", this.count);
this.toggleProgress(1);
this.pBar.setProgress(0, true);
},
clearNotification() {
Application.android.unregisterBroadcastReceiver("timer" + this.timer.id);
utils.TimerNotif.clear(this.timer.id);
},
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);
toggleProgress(n) {
this.togglePause(this.timer, n);
this.timer.isPaused ? this.resetInterval() : this.initTimer();
},
addPreset() {
let exist = this.timerPresets.some((e) => e.id == this.timer.id);
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() {
let delayInS = this.timerDelay.split(" ")[0] * 60;
ApplicationSettings.setNumber(`${this.timer.id}delay`, delayInS);
let progress = (100 / delayInS) * this.getTimeInSec;
addDelay() {
this.timer.done = 0;
let td = this.timerDelay;
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.setProgress(progress, delayInS);
this.clearNotification();
this.initTimer();
this.timerAlert();
},
// HELPERS
touch({ object, action }) {
object.className = action.match(/down|move/)
? "recipeTitle clickable fade"
: "recipeTitle clickable";
let classes = object.className;
classes = action.match(/down|move/)
? !classes.includes("fade")
? classes + " fade"
: classes
: classes.replace(/ fade/g, "");
if (action == "up") this.attachRecipe();
},
},
mounted() {
this.setProgress(
ApplicationSettings.getNumber(`${this.timer.id}progress`, 0),
ApplicationSettings.getNumber(`${this.timer.id}delay`, 0)
created() {
this.delay = ApplicationSettings.getNumber(`${this.timer.id}d`, 0);
this.count = ApplicationSettings.getNumber(
`${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) {
case "stop":
this.resetTimer();
break;
case "delay":
this.delay();
this.addDelay();
break;
case "dismiss":
this.removeTimer(this.timer.id, 1);
break;
}
});

View file

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

Binary file not shown.

View file

@ -290,13 +290,13 @@
"yld": "Ergebnis",
"buto": "Gesichert in %s",
"sysDefB": "Systemstandard + schwarz",
"tmr": "Eieruhr %s",
"tmr": "Kurzzeitwecker %s",
"strtBtn": "START",
"ntmr": "Neue Eieruhr",
"timer": "Eieruhr",
"ntmr": "Neuer Kurzzeitwecker",
"timer": "Kurzzeitwecker",
"sec": "s",
"tmrvbrt": "Eieruhrvibration",
"tmrSnd": "Eieruhrton",
"tmrvbrt": "Kurzzeitweckervibration",
"tmrSnd": "Kurzzeitweckerton",
"aTPrst": "Zu Voreinstellungen hinzugefügt",
"fwr": "für welches Rezept?",
"prstBtn": "Voreinstellungen",
@ -308,17 +308,17 @@
"minute": "Minute",
"dlyDur": "Verzögerungsdauer",
"delay": "Verzögerung",
"tmrRm": "Eieruhr entfernt",
"tmrRm": "Kurzzeitwecker entfernt",
"notifSetg": "Benachrichtigungseinstellungen",
"delPrst": "Du bist dabei, %s aus den Voreinstellungen zu löschen",
"prsts": "Voreinstellungen",
"ttv": "Zum Anzeigen tippen",
"dismissAll": "Alle Eieruhren verwerfen",
"dismissAll": "Alle Kurzzeitwecker verwerfen",
"dismiss": "Verwerfen",
"texp": "%s Eieruhren abgelaufen",
"texp": "%s Kurzzeitwecker abgelaufen",
"wDBy": "%1$s wurde um %2$s verzögert",
"prstTU": "Voreingestellte Zeit aktualisiert",
"ccwt": "Koche selbstbewusst mit Eieruhren!",
"ccwt": "Koche selbstbewusst mit Kurzzeitweckern!",
"gtD": "Zu Datum gehen",
"random": "Zufällig",
"oAP": "%1$s laufend, %2$s angehalten",
@ -333,5 +333,16 @@
"tue": "Dienstag",
"mon": "Montag",
"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",
"tdy": "Today",
"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",
"sun": "Sunday",
"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",
"sun": "Sunday",
"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",
"delPrst": "Está a punto de eliminar %s de los preajustes",
"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",
"sat": "sábado",
"fri": "viernes",
@ -328,9 +320,10 @@
"tue": "martes",
"mon": "lunes",
"sun": "domingo",
"calVM": "Modo de vista del calendario",
"oAP": "%1$s en curso, %2$s en pausa",
"prstTU": "Hora preestablecida actualizada",
"ccwt": "¡Cocine con confianza con los temporizadores!",
"gtD": "Ir a la fecha"
"d": "Día",
"wk": "Semana",
"mnth": "Mes",
"ystr": "Ayer",
"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",
"ystr": "Hier",
"tmrw": "Demain",
"tdy": "Oggi",
"tdy": "Aujourdhui",
"cpy": "copie",
"sat": "samedi",
"fri": "vendredi",
@ -332,6 +332,17 @@
"tue": "mardi",
"mon": "lundi",
"sun": "dimanche",
"d": "Jour",
"wk": "Semaine",
"mnth": "Mois",
"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!",
"gtD": "Aller à la date",
"random": "Aléatoire",
"tdy": "Aujourdhui",
"ystr": "Hier",
"tmrw": "Demain",
"tdy": "Aujourdhui",
"cpy": "copie",
"sat": "samedi",
"fri": "vendredi",
@ -332,6 +332,17 @@
"tue": "mardi",
"mon": "lundi",
"sun": "dimanche",
"d": "Jour",
"wk": "Semaine",
"mnth": "Mois",
"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",
"ystr": "Hier",
"tmrw": "Demain",
"tdy": "Oggi",
"tdy": "Aujourdhui",
"cpy": "copie",
"sat": "samedi",
"fri": "vendredi",
@ -332,6 +332,17 @@
"tue": "mardi",
"mon": "lundi",
"sun": "dimanche",
"d": "Jour",
"wk": "Semaine",
"mnth": "Mois",
"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 !",
"gtD": "Aller à la date",
"random": "Aléatoire",
"tdy": "Aujourdhui",
"ystr": "Hier",
"tmrw": "Demain",
"tdy": "Aujourdhui",
"cpy": "copie",
"sat": "samedi",
"fri": "vendredi",
@ -332,6 +332,17 @@
"tue": "mardi",
"mon": "lundi",
"sun": "dimanche",
"d": "Jour",
"wk": "Semaine",
"mnth": "Mois",
"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ì",
"mon": "lunedì",
"sun": "domenica",
"d": "Giorno",
"wk": "Settimana",
"mnth": "Mese",
"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",
"mon": "maandag",
"sun": "zondag",
"d": "Dag",
"wk": "Week",
"mnth": "Maand",
"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",
"delPrst": "Você está prestes a apagar %s das predefinições",
"tmrRm": "Timer removido",
"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"
"notifSetg": "Configurações de notificação"
}

View file

@ -321,17 +321,28 @@
"ccwt": "Cozinhe com segurança com temporizadores!",
"gtD": "Ir à data",
"random": "Aleatória",
"cpy": "cópia",
"oAP": "%1$s em curso, %2$s em pausa",
"calVM": "Modo de visualização de calendário",
"nvr": "Nunca",
"otaw": "Mais antigas que 1 semana",
"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",
"tmrw": "Amanhã",
"tdy": "Hoje",
"sat": "sábado",
"fri": "sexta-feira",
"thu": "quinta-feira",
"wed": "quarta-feira",
"tue": "terça-feira",
"mon": "segunda-feira",
"sun": "domingo"
"cpy": "cópia",
"sat": "Sábado",
"fri": "Sexta-feira",
"thu": "Quinta-feira",
"wed": "Quarta-feira",
"tue": "Terça-feira",
"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": "திங்கள்",
"sun": "ஞாயிறு",
"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 {
Application,
AndroidApplication,
ApplicationSettings,
Utils,
Device,
Color,
Frame,
} from '@nativescript/core'
import {
localize,
androidLaunchEventLocalizationHandler,
} from '@nativescript/localize'
import Vue from 'nativescript-vue'
import { localize } from '@nativescript/localize'
import EnRecipes from './components/EnRecipes.vue'
import EditRecipe from './components/EditRecipe.vue'
import MealPlanner from './components/MealPlanner.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'
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'
Vue.use(CollectionView)
import { StackLayout, GridLayout, DockLayout } from '@nativescript-rtl/ui'
Vue.registerElement('RStackLayout', () => StackLayout)
Vue.registerElement('RGridLayout', () => GridLayout)
Vue.registerElement('RDockLayout', () => DockLayout)
import { RGridLayout, RStackLayout, RDockLayout, RLabel } from './rtl-ui'
Vue.registerElement('RGridLayout', () => RGridLayout)
Vue.registerElement('RStackLayout', () => RStackLayout)
Vue.registerElement('RDockLayout', () => RDockLayout)
Vue.registerElement('RLabel', () => RLabel)
import { myMixin } from './shared/mixins'
Vue.mixin(myMixin)
const initFrame = () => {
const vm = store
//MAIN INIT
vm.commit('setTheme', ApplicationSettings.getString('appTheme', 'sysDef'))
if (!vm.state.recipes.length) vm.commit('initRecipes')
// MainInit
vm.commit('setTheme', ApplicationSettings.getString('theme', 'sysDef'))
vm.commit('initRecipes')
vm.commit('initMealPlans')
vm.commit('initListItems')
vm.commit('initTimerPresets')
@ -50,42 +48,11 @@ const initFrame = () => {
hasTimerSound ? JSON.parse(hasTimerSound) : utils.getTones().defaultTone
)
}
// INIT FRAME
const View = android.view.View as any
// InitFrame
const window = Application.android.startActivity.getWindow()
const decorView = window.getDecorView()
let sdkv = parseInt(Device.sdkVersion)
function setColors(color) {
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
utils.setBarColors(window, decorView, vm.state.theme)
Frame.topmost().className = vm.state.theme
}
const showOverLockscreen = () => {
let ctx = Utils.ad.getApplicationContext()
@ -103,12 +70,9 @@ const showOverLockscreen = () => {
}
}
const intentListener = ({ intent, android }: any) => {
console.log(intent, android)
let action = ((intent || android).getStringExtra('action') ||
(android && android.getAction())) as string
if (action) {
console.log(action)
switch (action) {
case 'new_recipe':
renderView = EditRecipe
@ -118,30 +82,47 @@ const intentListener = ({ intent, android }: any) => {
break
case 'timer':
renderView = CookingTimer
Vue.navigateTo(CookingTimer as any, {
animated: false,
})
break
case 'grocery':
renderView = GroceryList
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, {
animated: false,
})
ApplicationSettings.setNumber('isTimer', 1)
break
case 2:
Vue.navigateBack()
break
}
break
// case 'grocery':
// renderView = GroceryList
// break
}
}
}
Application.on(Application.resumeEvent, (args) => {
console.log('App Resume')
Application.on(Application.resumeEvent, () => {
showOverLockscreen()
if (
utils.sysLocale() !==
ApplicationSettings.getString('sysLocale', utils.sysLocale())
) {
Frame.reloadPage()
utils.updateLocale()
}
})
Application.on(Application.launchEvent, (args) => {
console.log('App Launch')
console.log('RTL', store.state.RTL)
utils.updateLocale()
store.commit('setRTL')
if (args.android) {
androidLaunchEventLocalizationHandler()
intentListener(args)
}
intentListener(args)
Application.android.on(
AndroidApplication.activityNewIntentEvent,
intentListener
@ -150,7 +131,6 @@ Application.on(Application.launchEvent, (args) => {
})
Application.on(Application.exitEvent, () => {
console.log('App Exit')
renderView = EnRecipes
Application.android.off(
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