Merge remote-tracking branch 'origin/main' into main
|
@ -1,15 +1,14 @@
|
||||||
import { TimerNotif } from './shared/utils'
|
import { TimerNotif } from './shared/utils'
|
||||||
const superProto = android.app.Service.prototype
|
const superProto = android.app.Service.prototype
|
||||||
|
|
||||||
android.app.Service.extend('com.tns.ForegroundService', {
|
android.app.Service.extend('com.tns.ForegroundService', {
|
||||||
onStartCommand: function(intent, flags, startId) {
|
onStartCommand: function(intent, flags, startId) {
|
||||||
console.log('onStartCommand')
|
|
||||||
superProto.onStartCommand.call(this, intent, flags, startId)
|
superProto.onStartCommand.call(this, intent, flags, startId)
|
||||||
return android.app.Service.START_STICKY
|
return android.app.Service.START_STICKY
|
||||||
},
|
},
|
||||||
onCreate: function() {
|
onCreate: function() {
|
||||||
console.log('onCreate')
|
|
||||||
superProto.onCreate.call(this)
|
superProto.onCreate.call(this)
|
||||||
this.startForeground(777, this.getNotification())
|
this.startForeground(6, this.getNotification())
|
||||||
},
|
},
|
||||||
onBind: function(intent) {
|
onBind: function(intent) {
|
||||||
return superProto.onBind.call(this, intent)
|
return superProto.onBind.call(this, intent)
|
||||||
|
@ -18,17 +17,16 @@ android.app.Service.extend('com.tns.ForegroundService', {
|
||||||
return superProto.onUnbind.call(this, intent)
|
return superProto.onUnbind.call(this, intent)
|
||||||
},
|
},
|
||||||
onDestroy: function() {
|
onDestroy: function() {
|
||||||
console.log('onDestroy')
|
|
||||||
this.stopForeground(true)
|
this.stopForeground(true)
|
||||||
},
|
},
|
||||||
getNotification: function() {
|
getNotification: function() {
|
||||||
return TimerNotif.getNotification(
|
return TimerNotif.getNotification(
|
||||||
{
|
{
|
||||||
bID: 'bringToFront',
|
bID: 'info',
|
||||||
cID: 'cti',
|
cID: 'cti',
|
||||||
cName: 'Cooking Timer info',
|
cName: 'Cooking Timer info',
|
||||||
description: `0 ongoing, 0 paused`,
|
description: `0 ongoing, 0 paused`,
|
||||||
nID: 777,
|
nID: 6,
|
||||||
priority: -2,
|
priority: -2,
|
||||||
sound: null,
|
sound: null,
|
||||||
title: 'EnRecipes is running',
|
title: 'EnRecipes is running',
|
||||||
|
|
186
app/app.scss
|
@ -37,7 +37,7 @@ Page {
|
||||||
@extend .tb;
|
@extend .tb;
|
||||||
@extend .tw;
|
@extend .tw;
|
||||||
font-size: 25;
|
font-size: 25;
|
||||||
padding: 16;
|
padding: 16 16 24;
|
||||||
}
|
}
|
||||||
.Light {
|
.Light {
|
||||||
color: $gray9;
|
color: $gray9;
|
||||||
|
@ -156,6 +156,7 @@ TextField.combField,
|
||||||
#searchBar {
|
#searchBar {
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// Elements
|
// Elements
|
||||||
TextField,
|
TextField,
|
||||||
|
@ -181,28 +182,15 @@ TextView {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
progress {
|
progress {
|
||||||
margin: 16;
|
color: $orange;
|
||||||
|
width: 100%;
|
||||||
|
height: 2;
|
||||||
background-color: $gray5;
|
background-color: $gray5;
|
||||||
}
|
}
|
||||||
Switch {
|
Switch {
|
||||||
background-color: $orange;
|
background-color: $orange;
|
||||||
off-background-color: $gray5;
|
off-background-color: $gray5;
|
||||||
}
|
}
|
||||||
// -----------------------------
|
|
||||||
// Side Drawer
|
|
||||||
.segment {
|
|
||||||
border-radius: 12;
|
|
||||||
margin: 0 4 0 0;
|
|
||||||
padding: 0 12;
|
|
||||||
.value {
|
|
||||||
padding-left: 8;
|
|
||||||
vertical-alignment: center;
|
|
||||||
}
|
|
||||||
&.select {
|
|
||||||
color: $orange;
|
|
||||||
@extend .hl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
button {
|
button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
@ -214,14 +202,13 @@ button {
|
||||||
@extend .fade;
|
@extend .fade;
|
||||||
}
|
}
|
||||||
&.ico {
|
&.ico {
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
width: 48;
|
width: 48;
|
||||||
height: 48;
|
height: 48;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
&.text {
|
&.text {
|
||||||
@extend .tb;
|
@extend .tb;
|
||||||
horizontal-alignment: left;
|
|
||||||
color: $orange;
|
color: $orange;
|
||||||
}
|
}
|
||||||
&.big {
|
&.big {
|
||||||
|
@ -235,12 +222,6 @@ button {
|
||||||
&.min {
|
&.min {
|
||||||
width: 40;
|
width: 40;
|
||||||
height: 40;
|
height: 40;
|
||||||
}
|
|
||||||
&.x {
|
|
||||||
width: 32;
|
|
||||||
height: 32;
|
|
||||||
min-width: 0;
|
|
||||||
margin: 0 0 0 4;
|
|
||||||
vertical-alignment: center;
|
vertical-alignment: center;
|
||||||
}
|
}
|
||||||
&.rate {
|
&.rate {
|
||||||
|
@ -249,8 +230,31 @@ button {
|
||||||
height: 32;
|
height: 32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ActivityIndicator {
|
||||||
|
width: 24;
|
||||||
|
height: 24;
|
||||||
|
margin: 12;
|
||||||
|
color: $orange;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// HOME
|
// Home
|
||||||
|
.segment {
|
||||||
|
border-radius: 12;
|
||||||
|
margin: 0 4 0 0;
|
||||||
|
padding: 0 12;
|
||||||
|
.value {
|
||||||
|
padding: 0 0 0 8;
|
||||||
|
vertical-alignment: center;
|
||||||
|
&.rtl {
|
||||||
|
padding: 0 8 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.select {
|
||||||
|
color: $orange;
|
||||||
|
@extend .hl;
|
||||||
|
}
|
||||||
|
}
|
||||||
.emptyState {
|
.emptyState {
|
||||||
padding: 16 16 8;
|
padding: 16 16 8;
|
||||||
label {
|
label {
|
||||||
|
@ -261,13 +265,18 @@ button {
|
||||||
font-size: 17;
|
font-size: 17;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// Recipe Items
|
// Recipe Item
|
||||||
.recipeItem {
|
.recipeItem {
|
||||||
padding: 8 16;
|
padding: 8 16;
|
||||||
.recipeInfo {
|
.recipeInfo {
|
||||||
vertical-alignment: center;
|
vertical-alignment: center;
|
||||||
padding: 0 0 4 8;
|
padding: 0 0 4 8;
|
||||||
|
&.rtl {
|
||||||
|
transform: none;
|
||||||
|
padding: 0 8 4 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
padding: 0 0 4;
|
padding: 0 0 4;
|
||||||
|
@ -275,6 +284,9 @@ button {
|
||||||
.attr {
|
.attr {
|
||||||
font-size: 10;
|
font-size: 10;
|
||||||
padding: 0 6 1 2;
|
padding: 0 6 1 2;
|
||||||
|
&.rtl {
|
||||||
|
padding: 0 2 1 6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.simple .recipeInfo {
|
.simple .recipeInfo {
|
||||||
|
@ -283,7 +295,6 @@ button {
|
||||||
.minimal .title {
|
.minimal .title {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid {
|
.grid {
|
||||||
padding: 8;
|
padding: 8;
|
||||||
.recipeInfo {
|
.recipeInfo {
|
||||||
|
@ -311,39 +322,34 @@ button {
|
||||||
.imgHolder {
|
.imgHolder {
|
||||||
border-radius: 12;
|
border-radius: 12;
|
||||||
}
|
}
|
||||||
// -----------------------------
|
|
||||||
// COOKING TIMER
|
|
||||||
|
|
||||||
.singleTimer {
|
// -----------------------------
|
||||||
padding: 8 16;
|
// CookingTimer
|
||||||
|
.timer {
|
||||||
|
padding: 8 16 0;
|
||||||
.info {
|
.info {
|
||||||
margin: 8;
|
margin: 8 8 8 16;
|
||||||
}
|
&.r {
|
||||||
.recipeTitle {
|
margin: 8 16 8 8;
|
||||||
horizontal-alignment: left;
|
}
|
||||||
// font-size: 12;
|
|
||||||
}
|
}
|
||||||
progress {
|
progress {
|
||||||
color: $orange;
|
|
||||||
width: 100%;
|
|
||||||
height: 4;
|
|
||||||
margin: 8 0 0;
|
margin: 8 0 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// SETTINGS
|
// Settings
|
||||||
.group-info {
|
.group-info {
|
||||||
padding: 16 16 16 72;
|
padding: 16 16 16 72;
|
||||||
line-height: 4;
|
line-height: 4;
|
||||||
}
|
}
|
||||||
.options-list {
|
.options {
|
||||||
.option {
|
.option {
|
||||||
vertical-align: center;
|
vertical-align: center;
|
||||||
padding: 14 8;
|
padding: 14 12;
|
||||||
margin: 0 16;
|
|
||||||
.ico {
|
.ico {
|
||||||
padding: 0;
|
margin: 0 24 0 12;
|
||||||
margin: 0 24 0 16;
|
|
||||||
}
|
}
|
||||||
.info,
|
.info,
|
||||||
.sub {
|
.sub {
|
||||||
|
@ -359,7 +365,7 @@ button {
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// ABOUT
|
// About
|
||||||
.app-info {
|
.app-info {
|
||||||
.icon {
|
.icon {
|
||||||
horizontal-alignment: center;
|
horizontal-alignment: center;
|
||||||
|
@ -372,8 +378,9 @@ button {
|
||||||
line-height: 4;
|
line-height: 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// VIEW RECIPE
|
// ViewRecipe
|
||||||
.photo {
|
.photo {
|
||||||
border-radius: 12;
|
border-radius: 12;
|
||||||
margin: 24 16 0 0;
|
margin: 24 16 0 0;
|
||||||
|
@ -396,15 +403,12 @@ button {
|
||||||
@extend .tw;
|
@extend .tw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.clickable {
|
|
||||||
color: $orange;
|
|
||||||
}
|
|
||||||
.ingredient {
|
.ingredient {
|
||||||
padding: 0 16;
|
padding: 0 16;
|
||||||
.value {
|
.value {
|
||||||
@extend .tw;
|
@extend .tw;
|
||||||
vertical-align: center;
|
vertical-align: center;
|
||||||
padding: 14 0 14 16;
|
padding: 14 16;
|
||||||
line-height: 4;
|
line-height: 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -416,13 +420,12 @@ button {
|
||||||
}
|
}
|
||||||
.value {
|
.value {
|
||||||
@extend .tw;
|
@extend .tw;
|
||||||
padding: 14 0 14 16;
|
padding: 14 16;
|
||||||
line-height: 4;
|
line-height: 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.done {
|
.done {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
// @extend .fade;
|
|
||||||
.value {
|
.value {
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
@ -443,8 +446,9 @@ button {
|
||||||
font-size: 12;
|
font-size: 12;
|
||||||
line-height: 4;
|
line-height: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// APPBAR
|
// AppBar
|
||||||
.appbar {
|
.appbar {
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
min-height: 56;
|
min-height: 56;
|
||||||
|
@ -461,13 +465,16 @@ button {
|
||||||
padding: 14 16;
|
padding: 14 16;
|
||||||
}
|
}
|
||||||
.fab {
|
.fab {
|
||||||
margin-left: 4;
|
margin-left: 8;
|
||||||
|
}
|
||||||
|
&.home {
|
||||||
|
margin: 8 8 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
padding: 4;
|
padding: 4;
|
||||||
margin-bottom: 0;
|
margin: 0 0 52;
|
||||||
horizontal-alignment: left;
|
horizontal-alignment: left;
|
||||||
.tool {
|
.tool {
|
||||||
padding: 0 12;
|
padding: 0 12;
|
||||||
|
@ -475,19 +482,20 @@ button {
|
||||||
vertical-alignment: center;
|
vertical-alignment: center;
|
||||||
}
|
}
|
||||||
.ico {
|
.ico {
|
||||||
padding-right: 8;
|
padding: 0 8 0 0;
|
||||||
|
&.rtl {
|
||||||
|
padding: 0 0 0 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fab {
|
.fab {
|
||||||
width: 48;
|
|
||||||
height: 48;
|
|
||||||
margin: 0 4 0 0;
|
|
||||||
border-radius: 12;
|
border-radius: 12;
|
||||||
background: $orange;
|
background: $orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// EDIT RECIPE
|
// EditRecipe
|
||||||
.sectionTitle {
|
.sectionTitle {
|
||||||
@extend .tb;
|
@extend .tb;
|
||||||
@extend .tw;
|
@extend .tw;
|
||||||
|
@ -504,8 +512,9 @@ button {
|
||||||
font-size: 17;
|
font-size: 17;
|
||||||
color: $orange;
|
color: $orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// MEAL PLANNER
|
// MealPlanner
|
||||||
.calendar {
|
.calendar {
|
||||||
padding: 0 8;
|
padding: 0 8;
|
||||||
.navBtn {
|
.navBtn {
|
||||||
|
@ -541,13 +550,13 @@ button {
|
||||||
}
|
}
|
||||||
.recipeTitle {
|
.recipeTitle {
|
||||||
@extend .tw;
|
@extend .tw;
|
||||||
text-align: left;
|
|
||||||
padding: 16 8;
|
padding: 16 8;
|
||||||
line-height: 4;
|
line-height: 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// DIALOGS
|
// Dialogs
|
||||||
.modal {
|
.modal {
|
||||||
max-width: 320;
|
max-width: 320;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -567,13 +576,12 @@ button {
|
||||||
padding: 0 16 8;
|
padding: 0 16 8;
|
||||||
}
|
}
|
||||||
ListPicker {
|
ListPicker {
|
||||||
width: 25%;
|
width: 30%;
|
||||||
height: 128;
|
height: 144;
|
||||||
margin: 16 0;
|
margin: 16 0;
|
||||||
}
|
}
|
||||||
.listItem {
|
.listItem {
|
||||||
@extend .tw;
|
@extend .tw;
|
||||||
width: 100%;
|
|
||||||
letter-spacing: 0;
|
letter-spacing: 0;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
line-height: 4;
|
line-height: 4;
|
||||||
|
@ -607,32 +615,9 @@ button {
|
||||||
padding: 16;
|
padding: 16;
|
||||||
line-height: 4;
|
line-height: 4;
|
||||||
}
|
}
|
||||||
// -----------------------------
|
|
||||||
ActivityIndicator {
|
|
||||||
width: 24;
|
|
||||||
height: 24;
|
|
||||||
margin: 12;
|
|
||||||
color: $orange;
|
|
||||||
}
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// Transitions
|
// Transitions
|
||||||
.blink {
|
|
||||||
animation-name: blink;
|
|
||||||
animation-duration: 1s;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
animation-timing-function: ease;
|
|
||||||
}
|
|
||||||
@keyframes blink {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.hl {
|
.hl {
|
||||||
animation-name: hl;
|
animation-name: hl;
|
||||||
animation-duration: 0.2s;
|
animation-duration: 0.2s;
|
||||||
|
@ -661,3 +646,18 @@ ActivityIndicator {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------
|
||||||
|
// Helpers
|
||||||
|
.rtl {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
.clickable {
|
||||||
|
color: $orange;
|
||||||
|
}
|
||||||
|
.hal {
|
||||||
|
horizontal-alignment: left;
|
||||||
|
&.r {
|
||||||
|
horizontal-alignment: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<Page @loaded="onPageLoad" actionBarHidden="true">
|
<Page @loaded="pgLoad" actionBarHidden="true">
|
||||||
<GridLayout rows="*, auto" columns="auto, *">
|
<RGridLayout :isRtl="RTL" rows="*, auto" columns="auto, *">
|
||||||
<OptionsList title="intf" :items="items" />
|
<OptionsList title="intf" :items="items" />
|
||||||
<GridLayout row="1" class="appbar" rows="*" columns="auto, *">
|
<GridLayout
|
||||||
|
:isRtl="RTL"
|
||||||
|
row="1"
|
||||||
|
class="appbar"
|
||||||
|
rows="*"
|
||||||
|
columns="auto, *"
|
||||||
|
>
|
||||||
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
|
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</GridLayout>
|
</RGridLayout>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -16,7 +22,7 @@ import {
|
||||||
Device,
|
Device,
|
||||||
Frame,
|
Frame,
|
||||||
} from "@nativescript/core";
|
} from "@nativescript/core";
|
||||||
import { localize, overrideLocale } from "@nativescript/localize";
|
import { localize } from "@nativescript/localize";
|
||||||
import Action from "../modals/Action";
|
import Action from "../modals/Action";
|
||||||
import Confirm from "../modals/Confirm";
|
import Confirm from "../modals/Confirm";
|
||||||
import OptionsList from "../sub/OptionsList";
|
import OptionsList from "../sub/OptionsList";
|
||||||
|
@ -27,11 +33,11 @@ export default {
|
||||||
components: { OptionsList },
|
components: { OptionsList },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
appLanguage: "English",
|
applang: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["icon", "language", "appTheme", "layout"]),
|
...mapState(["icon", "language", "appTheme", "layout", "RTL"]),
|
||||||
items() {
|
items() {
|
||||||
return [
|
return [
|
||||||
{},
|
{},
|
||||||
|
@ -39,23 +45,21 @@ export default {
|
||||||
type: "list",
|
type: "list",
|
||||||
icon: "lang",
|
icon: "lang",
|
||||||
title: "lang",
|
title: "lang",
|
||||||
subTitle: this.appLanguage,
|
subTitle: this.applang,
|
||||||
action: this.selectAppLanguage,
|
action: this.setAppLang,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "list",
|
type: "list",
|
||||||
icon: "theme",
|
icon: "theme",
|
||||||
title: "Theme",
|
title: "Theme",
|
||||||
subTitle: localize(
|
subTitle: ApplicationSettings.getString("appTheme", "sysDef"),
|
||||||
ApplicationSettings.getString("appTheme", "sysDef")
|
|
||||||
),
|
|
||||||
action: this.selectThemes,
|
action: this.selectThemes,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "list",
|
type: "list",
|
||||||
icon: "layout",
|
icon: "layout",
|
||||||
title: "listVM",
|
title: "listVM",
|
||||||
subTitle: localize(this.layout),
|
subTitle: this.layout,
|
||||||
action: this.setLayoutMode,
|
action: this.setLayoutMode,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
|
@ -63,12 +67,12 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(["setTheme", "setLayout"]),
|
...mapActions(["setTheme", "setLayout", "setRTL"]),
|
||||||
onPageLoad({ object }) {
|
pgLoad({ object }) {
|
||||||
object.bindingContext = new Observable();
|
object.bindingContext = new Observable();
|
||||||
},
|
},
|
||||||
// LANGUAGE SELECTION
|
// LANGUAGE SELECTION
|
||||||
selectAppLanguage() {
|
setAppLang() {
|
||||||
let languages = this.language.map((e) => e.title);
|
let languages = this.language.map((e) => e.title);
|
||||||
this.$showModal(Action, {
|
this.$showModal(Action, {
|
||||||
props: {
|
props: {
|
||||||
|
@ -76,26 +80,19 @@ export default {
|
||||||
list: [...languages],
|
list: [...languages],
|
||||||
},
|
},
|
||||||
}).then((action) => {
|
}).then((action) => {
|
||||||
if (action && this.appLanguage !== action) {
|
if (action && this.applang !== action) {
|
||||||
let currentLocale = Device.language.split("-")[0];
|
let currentLocale = ApplicationSettings.getString(
|
||||||
|
"appLocale",
|
||||||
|
"none"
|
||||||
|
).split("-");
|
||||||
let locale = this.language.filter((e) => e.title === action)[0]
|
let locale = this.language.filter((e) => e.title === action)[0]
|
||||||
.locale;
|
.locale;
|
||||||
if (currentLocale !== locale) {
|
if (currentLocale !== locale) {
|
||||||
this.$showModal(Confirm, {
|
this.applang = action;
|
||||||
props: {
|
ApplicationSettings.setString("applang", action);
|
||||||
title: "appRst",
|
ApplicationSettings.setString("appLocale", locale);
|
||||||
description: localize("nLangInfo"),
|
utils.updateLocale();
|
||||||
cancelButtonText: "cBtn",
|
this.setRTL();
|
||||||
okButtonText: "rst",
|
|
||||||
},
|
|
||||||
}).then((result) => {
|
|
||||||
if (result) {
|
|
||||||
this.appLanguage = action;
|
|
||||||
ApplicationSettings.setString("appLanguage", action);
|
|
||||||
overrideLocale(locale);
|
|
||||||
setTimeout(utils.restartApp, 250);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -111,7 +108,7 @@ export default {
|
||||||
if (
|
if (
|
||||||
action &&
|
action &&
|
||||||
(ApplicationSettings.getString("appTheme") != this.appTheme
|
(ApplicationSettings.getString("appTheme") != this.appTheme
|
||||||
? true
|
? 1
|
||||||
: this.appTheme != action)
|
: this.appTheme != action)
|
||||||
) {
|
) {
|
||||||
this.setTheme(action);
|
this.setTheme(action);
|
||||||
|
@ -136,10 +133,7 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.appLanguage = ApplicationSettings.getString(
|
this.applang = ApplicationSettings.getString("applang", "sysDef");
|
||||||
"appLanguage",
|
|
||||||
localize("sysDef")
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -320,5 +320,21 @@
|
||||||
"texp": "%s timers expired",
|
"texp": "%s timers expired",
|
||||||
"dismiss": "Dismiss",
|
"dismiss": "Dismiss",
|
||||||
"dismissAll": "Dismiss all timers",
|
"dismissAll": "Dismiss all timers",
|
||||||
"ttv": "Tap to view"
|
"ttv": "Tap to view",
|
||||||
|
"oAP": "%1$s ongoing, %2$s paused",
|
||||||
|
"calVM": "Calendar view mode",
|
||||||
|
"mnthly": "Monthly",
|
||||||
|
"wkly": "Weekly",
|
||||||
|
"dly": "Daily",
|
||||||
|
"sun": "Sunday",
|
||||||
|
"mon": "Monday",
|
||||||
|
"tue": "Tuesday",
|
||||||
|
"wed": "Wednesday",
|
||||||
|
"thu": "Thursday",
|
||||||
|
"fri": "Friday",
|
||||||
|
"sat": "Saturday",
|
||||||
|
"cpy": "copy",
|
||||||
|
"tdy": "Today",
|
||||||
|
"tmrw": "Tomorrow",
|
||||||
|
"ystr": "Yesterday"
|
||||||
}
|
}
|
||||||
|
|
196
app/main.ts
|
@ -1,15 +1,98 @@
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
AndroidApplication,
|
||||||
|
ApplicationSettings,
|
||||||
|
Utils,
|
||||||
|
Device,
|
||||||
|
Color,
|
||||||
|
Frame,
|
||||||
|
} from '@nativescript/core'
|
||||||
import {
|
import {
|
||||||
localize,
|
localize,
|
||||||
androidLaunchEventLocalizationHandler,
|
androidLaunchEventLocalizationHandler,
|
||||||
} from '@nativescript/localize'
|
} from '@nativescript/localize'
|
||||||
import { Application, AndroidApplication, Utils } from '@nativescript/core'
|
import Vue from 'nativescript-vue'
|
||||||
|
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 * as utils from '~/shared/utils'
|
||||||
|
|
||||||
const keepScreenOn = () => {
|
export const EventBus = new Vue()
|
||||||
|
|
||||||
|
let renderView: any = 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 { 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')
|
||||||
|
vm.commit('initMealPlans')
|
||||||
|
vm.commit('initListItems')
|
||||||
|
vm.commit('initTimerPresets')
|
||||||
|
if (!Object.keys(vm.state.timerSound).length) {
|
||||||
|
let hasTimerSound = ApplicationSettings.getString('timerSound', null)
|
||||||
|
vm.commit(
|
||||||
|
'setTimerSound',
|
||||||
|
hasTimerSound ? JSON.parse(hasTimerSound) : utils.getTones().defaultTone
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// INIT FRAME
|
||||||
|
const View = android.view.View as any
|
||||||
|
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
|
||||||
|
}
|
||||||
|
const showOverLockscreen = () => {
|
||||||
let ctx = Utils.ad.getApplicationContext()
|
let ctx = Utils.ad.getApplicationContext()
|
||||||
const pm = ctx.getSystemService(android.content.Context.POWER_SERVICE)
|
const pm = ctx.getSystemService(android.content.Context.POWER_SERVICE)
|
||||||
let isScreenOff = !pm.isInteractive()
|
let isScreenOff = !pm.isInteractive()
|
||||||
if (isScreenOff) {
|
if (isScreenOff) {
|
||||||
console.log('keepScreenOn')
|
console.log('showOverLockscreen')
|
||||||
const window = Application.android.startActivity.getWindow()
|
const window = Application.android.startActivity.getWindow()
|
||||||
const windowMgr = android.view.WindowManager
|
const windowMgr = android.view.WindowManager
|
||||||
const flags =
|
const flags =
|
||||||
|
@ -17,42 +100,63 @@ const keepScreenOn = () => {
|
||||||
windowMgr.LayoutParams.FLAG_TURN_SCREEN_ON |
|
windowMgr.LayoutParams.FLAG_TURN_SCREEN_ON |
|
||||||
windowMgr.LayoutParams.FLAG_KEEP_SCREEN_ON
|
windowMgr.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||||
window.addFlags(flags)
|
window.addFlags(flags)
|
||||||
function clearFlags(args) {
|
|
||||||
args.cancel = true
|
|
||||||
window.clearFlags(flags)
|
|
||||||
Application.android.off(
|
|
||||||
AndroidApplication.activityBackPressedEvent,
|
|
||||||
clearFlags
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Application.android.on(
|
|
||||||
AndroidApplication.activityBackPressedEvent,
|
|
||||||
clearFlags
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Application.on(Application.resumeEvent, keepScreenOn)
|
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
|
||||||
|
break
|
||||||
|
case 'planner':
|
||||||
|
renderView = MealPlanner
|
||||||
|
break
|
||||||
|
case 'timer':
|
||||||
|
renderView = CookingTimer
|
||||||
|
Vue.navigateTo(CookingTimer as any, {
|
||||||
|
animated: false,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'grocery':
|
||||||
|
renderView = GroceryList
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Application.on(Application.resumeEvent, (args) => {
|
||||||
|
console.log('App Resume')
|
||||||
|
showOverLockscreen()
|
||||||
|
})
|
||||||
|
|
||||||
Application.on(Application.launchEvent, (args) => {
|
Application.on(Application.launchEvent, (args) => {
|
||||||
console.log('launching')
|
console.log('App Launch')
|
||||||
|
console.log('RTL', store.state.RTL)
|
||||||
|
store.commit('setRTL')
|
||||||
if (args.android) {
|
if (args.android) {
|
||||||
androidLaunchEventLocalizationHandler()
|
androidLaunchEventLocalizationHandler()
|
||||||
intentListener(args)
|
intentListener(args)
|
||||||
}
|
}
|
||||||
|
Application.android.on(
|
||||||
|
AndroidApplication.activityNewIntentEvent,
|
||||||
|
intentListener
|
||||||
|
)
|
||||||
|
Frame.on(Frame.loadedEvent, initFrame)
|
||||||
})
|
})
|
||||||
|
|
||||||
import Vue from 'nativescript-vue'
|
Application.on(Application.exitEvent, () => {
|
||||||
import EnRecipes from './components/EnRecipes.vue'
|
console.log('App Exit')
|
||||||
import CookingTimer from './components/CookingTimer.vue'
|
renderView = EnRecipes
|
||||||
import store from './store'
|
Application.android.off(
|
||||||
|
AndroidApplication.activityNewIntentEvent,
|
||||||
export const EventBus = new Vue()
|
intentListener
|
||||||
|
)
|
||||||
import CollectionView from '@nativescript-community/ui-collectionview/vue'
|
})
|
||||||
Vue.use(CollectionView)
|
|
||||||
|
|
||||||
import { lvMixin } from './shared/mixins'
|
|
||||||
Vue.mixin(lvMixin)
|
|
||||||
|
|
||||||
Vue.config.silent = false
|
Vue.config.silent = false
|
||||||
|
|
||||||
|
@ -60,37 +164,5 @@ Vue.filter('L', localize)
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
store,
|
store,
|
||||||
render: (h) => h(EnRecipes),
|
render: (h) => h('Frame', [h(renderView)]),
|
||||||
}).$start()
|
}).$start()
|
||||||
|
|
||||||
const intentListener = ({ intent, android }: any) => {
|
|
||||||
let ct = 'CookingTimer'
|
|
||||||
let action = (intent || android).getStringExtra('action')
|
|
||||||
console.log('calling: ', action)
|
|
||||||
if (action == 'open_timer' && store.state.currentComponent != ct) {
|
|
||||||
let openTimer = setInterval(() => {
|
|
||||||
let comp = store.state.currentComponent
|
|
||||||
if (comp == ct) clearInterval(openTimer)
|
|
||||||
else {
|
|
||||||
if (comp == 'CTSettings') Vue.navigateBack()
|
|
||||||
else {
|
|
||||||
Vue.navigateTo(CookingTimer)
|
|
||||||
store.commit('setComponent', 'CookingTimer')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 250)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Application.on(Application.launchEvent, () => {
|
|
||||||
Application.android.on(
|
|
||||||
AndroidApplication.activityNewIntentEvent,
|
|
||||||
intentListener
|
|
||||||
)
|
|
||||||
})
|
|
||||||
Application.on(Application.exitEvent, () => {
|
|
||||||
store.commit('setComponent', 'EnRecipes')
|
|
||||||
Application.android.off(
|
|
||||||
AndroidApplication.activityNewIntentEvent,
|
|
||||||
intentListener
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
|
@ -11,13 +11,14 @@
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" tools:node="remove" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" tools:node="remove" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" tools:node="remove" />
|
<uses-permission android:name="android.permission.INTERNET" tools:node="remove" />
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove" />
|
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove" />
|
||||||
<application android:extractNativeLibs="true" android:name="com.tns.NativeScriptApplication" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:hardwareAccelerated="true" android:largeHeap="true" android:theme="@style/AppTheme">
|
<application android:extractNativeLibs="true" android:name="com.tns.NativeScriptApplication" android:allowBackup="true" android:icon="@drawable/app_icon" android:label="@string/app_name" android:supportsRtl="true" android:hardwareAccelerated="true" android:largeHeap="true" android:theme="@style/AppTheme">
|
||||||
<activity android:name="com.tns.NativeScriptActivity" android:label="@string/title_activity_kimera" android:launchMode="singleInstance" android:screenOrientation="userPortrait" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|locale|uiMode" android:theme="@style/LaunchScreenTheme" android:windowSoftInputMode="adjustResize">
|
<activity android:name="com.tns.NativeScriptActivity" android:label="@string/title_activity_kimera" android:launchMode="singleTop" android:screenOrientation="userPortrait" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|locale|uiMode" android:theme="@style/LaunchScreenTheme" android:windowSoftInputMode="adjustResize">
|
||||||
<meta-data android:name="SET_THEME_ON_LAUNCH" android:resource="@style/AppTheme" />
|
<meta-data android:name="SET_THEME_ON_LAUNCH" android:resource="@style/AppTheme" />
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="com.tns.ErrorReportActivity" />
|
<activity android:name="com.tns.ErrorReportActivity" />
|
||||||
<service android:name="com.tns.ForegroundService" android:enabled="true" android:exported="false" />
|
<service android:name="com.tns.ForegroundService" android:enabled="true" android:exported="false" />
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@drawable/app_icon_fg" />
|
||||||
<background android:drawable="@color/white" />
|
<background android:drawable="@color/white" />
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<foreground android:drawable="@drawable/sc_add_fg" />
|
||||||
|
<background android:drawable="@color/orange" />
|
||||||
|
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<foreground android:drawable="@drawable/sc_grocery_fg" />
|
||||||
|
<background android:drawable="@color/orange" />
|
||||||
|
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<foreground android:drawable="@drawable/sc_planner_fg" />
|
||||||
|
<background android:drawable="@color/orange" />
|
||||||
|
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<foreground android:drawable="@drawable/sc_timer_fg" />
|
||||||
|
<background android:drawable="@color/orange" />
|
||||||
|
</adaptive-icon>
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 954 B After Width: | Height: | Size: 954 B |
Before Width: | Height: | Size: 938 B After Width: | Height: | Size: 938 B |
BIN
app/resources/Android/src/main/res/drawable-hdpi/sc_add.webp
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/resources/Android/src/main/res/drawable-hdpi/sc_add_fg.webp
Normal file
After Width: | Height: | Size: 780 B |
BIN
app/resources/Android/src/main/res/drawable-hdpi/sc_grocery.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 932 B |
BIN
app/resources/Android/src/main/res/drawable-hdpi/sc_planner.webp
Normal file
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 940 B |
BIN
app/resources/Android/src/main/res/drawable-hdpi/sc_timer.webp
Normal file
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 860 B After Width: | Height: | Size: 860 B |
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 852 B |
BIN
app/resources/Android/src/main/res/drawable-mdpi/sc_add.webp
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
app/resources/Android/src/main/res/drawable-mdpi/sc_add_fg.webp
Normal file
After Width: | Height: | Size: 758 B |
BIN
app/resources/Android/src/main/res/drawable-mdpi/sc_grocery.webp
Normal file
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 906 B |
BIN
app/resources/Android/src/main/res/drawable-mdpi/sc_planner.webp
Normal file
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 870 B |
BIN
app/resources/Android/src/main/res/drawable-mdpi/sc_timer.webp
Normal file
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1,016 B After Width: | Height: | Size: 1,016 B |
Before Width: | Height: | Size: 1,016 B After Width: | Height: | Size: 1,016 B |
BIN
app/resources/Android/src/main/res/drawable-xhdpi/sc_add.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/resources/Android/src/main/res/drawable-xhdpi/sc_add_fg.webp
Normal file
After Width: | Height: | Size: 846 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1,018 B |
BIN
app/resources/Android/src/main/res/drawable-xhdpi/sc_timer.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
app/resources/Android/src/main/res/drawable-xxhdpi/sc_add.webp
Normal file
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 912 B |
After Width: | Height: | Size: 2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2 KiB |
After Width: | Height: | Size: 1.1 KiB |
BIN
app/resources/Android/src/main/res/drawable-xxhdpi/sc_timer.webp
Normal file
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
BIN
app/resources/Android/src/main/res/drawable-xxxhdpi/sc_add.webp
Normal file
After Width: | Height: | Size: 2 KiB |
After Width: | Height: | Size: 1,006 B |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.5 KiB |
|
@ -6,11 +6,18 @@ import {
|
||||||
Color,
|
Color,
|
||||||
path,
|
path,
|
||||||
knownFolders,
|
knownFolders,
|
||||||
TimerInfo,
|
|
||||||
} from '@nativescript/core'
|
} from '@nativescript/core'
|
||||||
|
import { localize } from '@nativescript/localize'
|
||||||
|
|
||||||
let timerOne
|
let timerOne
|
||||||
declare const global, android, androidx, com, java, Array: any
|
declare const global, android, androidx, com, java, Array: any
|
||||||
|
|
||||||
|
const PowerManager = android.os.PowerManager
|
||||||
|
const pm = Utils.android
|
||||||
|
.getApplicationContext()
|
||||||
|
.getSystemService(android.content.Context.POWER_SERVICE)
|
||||||
|
const wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 'Timers')
|
||||||
|
|
||||||
export const restartApp = () => {
|
export const restartApp = () => {
|
||||||
const ctx = Utils.ad.getApplicationContext()
|
const ctx = Utils.ad.getApplicationContext()
|
||||||
let mStartActivity = new android.content.Intent(
|
let mStartActivity = new android.content.Intent(
|
||||||
|
@ -60,14 +67,14 @@ export const vibrate = (duration) => {
|
||||||
if (vibratorService.hasVibrator()) vibratorService.vibrate(duration)
|
if (vibratorService.hasVibrator()) vibratorService.vibrate(duration)
|
||||||
}
|
}
|
||||||
export const timer = (dur, callback) => {
|
export const timer = (dur, callback) => {
|
||||||
clearInterval(timerOne)
|
|
||||||
callback(true)
|
callback(true)
|
||||||
|
clearInterval(timerOne)
|
||||||
timerOne = setInterval(() => {
|
timerOne = setInterval(() => {
|
||||||
dur--
|
dur--
|
||||||
callback(true)
|
callback(true)
|
||||||
if (dur == 0) {
|
if (dur == 0) {
|
||||||
callback(false)
|
|
||||||
clearInterval(timerOne)
|
clearInterval(timerOne)
|
||||||
|
callback(false)
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
@ -285,12 +292,12 @@ export const shareText = (text, subject) => {
|
||||||
intent.putExtra(android.content.Intent.EXTRA_TEXT, text)
|
intent.putExtra(android.content.Intent.EXTRA_TEXT, text)
|
||||||
share(intent, subject)
|
share(intent, subject)
|
||||||
}
|
}
|
||||||
export const shareImage = (image, subject) => {
|
export const shareImage = (image, subject, title) => {
|
||||||
let ctx = Application.android.context
|
let ctx = Application.android.context
|
||||||
const intent = getSendIntent('image/jpeg')
|
const intent = getSendIntent('image/jpeg')
|
||||||
const baos = new java.io.ByteArrayOutputStream()
|
const baos = new java.io.ByteArrayOutputStream()
|
||||||
image.android.compress(android.graphics.Bitmap.CompressFormat.JPEG, 100, baos)
|
image.android.compress(android.graphics.Bitmap.CompressFormat.JPEG, 100, baos)
|
||||||
const tmpFile = new java.io.File(ctx.getExternalCacheDir(), 'EnRecipes.jpg')
|
const tmpFile = new java.io.File(ctx.getCacheDir(), `${title}.jpg`)
|
||||||
const fos = new java.io.FileOutputStream(tmpFile)
|
const fos = new java.io.FileOutputStream(tmpFile)
|
||||||
fos.write(baos.toByteArray())
|
fos.write(baos.toByteArray())
|
||||||
fos.flush()
|
fos.flush()
|
||||||
|
@ -304,6 +311,14 @@ export const shareImage = (image, subject) => {
|
||||||
share(intent, subject)
|
share(intent, subject)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const keepScreenOn = (bool) => {
|
||||||
|
let ctx =
|
||||||
|
Application.android.foregroundActivity || Application.android.startActivity
|
||||||
|
let window = ctx.getWindow()
|
||||||
|
let flag = android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||||
|
bool ? window.addFlags(flag) : window.clearFlags(flag)
|
||||||
|
}
|
||||||
|
|
||||||
// TIMER NOTIFICATION
|
// TIMER NOTIFICATION
|
||||||
export class TimerNotif {
|
export class TimerNotif {
|
||||||
static getIcon(ctx, icon) {
|
static getIcon(ctx, icon) {
|
||||||
|
@ -322,26 +337,25 @@ export class TimerNotif {
|
||||||
// nID ? NotifySrv.cancel(nID) : NotifySrv.cancelAll()
|
// nID ? NotifySrv.cancel(nID) : NotifySrv.cancelAll()
|
||||||
NotifySrv.cancel(nID)
|
NotifySrv.cancel(nID)
|
||||||
}
|
}
|
||||||
|
|
||||||
static getNotification(
|
static getNotification(
|
||||||
{
|
{
|
||||||
|
multi,
|
||||||
actions,
|
actions,
|
||||||
bID,
|
bID,
|
||||||
cID,
|
cID,
|
||||||
cName,
|
cName,
|
||||||
description,
|
description,
|
||||||
nID,
|
|
||||||
priority,
|
priority,
|
||||||
sound,
|
sound,
|
||||||
title,
|
title,
|
||||||
vibrate,
|
vibrate,
|
||||||
}: {
|
}: {
|
||||||
|
multi?: boolean
|
||||||
actions?: boolean
|
actions?: boolean
|
||||||
bID: string
|
bID: string
|
||||||
cID: string
|
cID: string
|
||||||
cName: string
|
cName: string
|
||||||
description: string
|
description: string
|
||||||
nID: number
|
|
||||||
priority: number
|
priority: number
|
||||||
sound: string
|
sound: string
|
||||||
title: string
|
title: string
|
||||||
|
@ -361,7 +375,6 @@ export class TimerNotif {
|
||||||
if (sdkv >= 26) {
|
if (sdkv >= 26) {
|
||||||
const importance =
|
const importance =
|
||||||
priority > 0 ? NotifyMgr.IMPORTANCE_HIGH : NotifyMgr.IMPORTANCE_MIN
|
priority > 0 ? NotifyMgr.IMPORTANCE_HIGH : NotifyMgr.IMPORTANCE_MIN
|
||||||
console.log(priority, importance)
|
|
||||||
const AudioAttributes = android.media.AudioAttributes
|
const AudioAttributes = android.media.AudioAttributes
|
||||||
const audioAttributes = new AudioAttributes.Builder()
|
const audioAttributes = new AudioAttributes.Builder()
|
||||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||||
|
@ -386,7 +399,7 @@ export class TimerNotif {
|
||||||
const PendingIntent = android.app.PendingIntent
|
const PendingIntent = android.app.PendingIntent
|
||||||
|
|
||||||
const mainInt = new Intent(ctx, com.tns.NativeScriptActivity.class)
|
const mainInt = new Intent(ctx, com.tns.NativeScriptActivity.class)
|
||||||
mainInt.putExtra('action', 'open_timer')
|
mainInt.putExtra('action', 'timer')
|
||||||
const mainPInt = PendingIntent.getActivity(
|
const mainPInt = PendingIntent.getActivity(
|
||||||
ctx,
|
ctx,
|
||||||
1,
|
1,
|
||||||
|
@ -395,10 +408,15 @@ export class TimerNotif {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Action intent
|
// Action intent
|
||||||
let actionInt1, actionInt2, actionPInt1, actionPInt2
|
let actionInt1,
|
||||||
|
actionInt2,
|
||||||
|
actionInt3,
|
||||||
|
actionPInt1,
|
||||||
|
actionPInt2,
|
||||||
|
actionPInt3
|
||||||
if (actions) {
|
if (actions) {
|
||||||
actionInt1 = new Intent(bID)
|
actionInt1 = new Intent(bID)
|
||||||
actionInt1.putExtra('action', 'stop')
|
actionInt1.putExtra('action', 'delay')
|
||||||
actionPInt1 = PendingIntent.getBroadcast(
|
actionPInt1 = PendingIntent.getBroadcast(
|
||||||
ctx,
|
ctx,
|
||||||
2,
|
2,
|
||||||
|
@ -406,18 +424,26 @@ export class TimerNotif {
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
actionInt2 = new Intent(bID)
|
actionInt2 = new Intent(bID)
|
||||||
actionInt2.putExtra('action', 'delay')
|
actionInt2.putExtra('action', 'dismiss')
|
||||||
actionPInt2 = PendingIntent.getBroadcast(
|
actionPInt2 = PendingIntent.getBroadcast(
|
||||||
ctx,
|
ctx,
|
||||||
3,
|
3,
|
||||||
actionInt2,
|
actionInt2,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
actionInt3 = new Intent(bID)
|
||||||
|
actionInt3.putExtra('action', 'dismissAll')
|
||||||
|
actionPInt3 = PendingIntent.getBroadcast(
|
||||||
|
ctx,
|
||||||
|
4,
|
||||||
|
actionInt3,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CREATE NOTIFICATION
|
// CREATE NOTIFICATION
|
||||||
|
|
||||||
let icon = TimerNotif.getIcon(ctx, 'ic_stat_notify_silhouette')
|
let icon = this.getIcon(ctx, 'notify_icon_sil')
|
||||||
let builder = new NotificationCompat.Builder(ctx, cID)
|
let builder = new NotificationCompat.Builder(ctx, cID)
|
||||||
.setColor(new Color('#ff5200').android)
|
.setColor(new Color('#ff5200').android)
|
||||||
.setContentIntent(mainPInt)
|
.setContentIntent(mainPInt)
|
||||||
|
@ -434,10 +460,12 @@ export class TimerNotif {
|
||||||
if (description) builder.setContentText(description)
|
if (description) builder.setContentText(description)
|
||||||
if (vibrate) builder.setVibrate([500, 1000])
|
if (vibrate) builder.setVibrate([500, 1000])
|
||||||
if (actions) {
|
if (actions) {
|
||||||
builder.setDeleteIntent(actionPInt2)
|
|
||||||
builder.setFullScreenIntent(mainPInt, true)
|
builder.setFullScreenIntent(mainPInt, true)
|
||||||
builder.addAction(null, 'Stop', actionPInt1)
|
if (multi) builder.addAction(null, localize('dismissAll'), actionPInt3)
|
||||||
builder.addAction(null, 'Delay', actionPInt2)
|
else {
|
||||||
|
builder.addAction(null, localize('delay'), actionPInt1)
|
||||||
|
builder.addAction(null, localize('dismiss'), actionPInt2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let notification = builder.build()
|
let notification = builder.build()
|
||||||
notification.flags =
|
notification.flags =
|
||||||
|
@ -450,7 +478,45 @@ export class TimerNotif {
|
||||||
const NotifySrv = ctx.getSystemService(
|
const NotifySrv = ctx.getSystemService(
|
||||||
android.content.Context.NOTIFICATION_SERVICE
|
android.content.Context.NOTIFICATION_SERVICE
|
||||||
)
|
)
|
||||||
NotifySrv.notify(data.nID, TimerNotif.getNotification(data, ctx))
|
NotifySrv.notify(data.nID, this.getNotification(data, ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class Printer {
|
||||||
|
static PrintPackage = global.androidx.print
|
||||||
|
static isSupported() {
|
||||||
|
return this.PrintPackage.PrintHelper.systemSupportsPrint()
|
||||||
|
}
|
||||||
|
static print(view) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
let img: any
|
||||||
|
img = android.graphics.Bitmap.createBitmap(
|
||||||
|
view.getMeasuredWidth(),
|
||||||
|
view.getMeasuredHeight(),
|
||||||
|
android.graphics.Bitmap.Config.ARGB_8888
|
||||||
|
)
|
||||||
|
view.android.draw(new android.graphics.Canvas(img))
|
||||||
|
this.printImage(img).then(resolve, reject)
|
||||||
|
} catch (e) {
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
static printImage(img) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
let callback = (success) => resolve(success)
|
||||||
|
let printHelper = new this.PrintPackage.PrintHelper(
|
||||||
|
Application.android.foregroundActivity
|
||||||
|
)
|
||||||
|
printHelper.setScaleMode(this.PrintPackage.PrintHelper.SCALE_MODE_FIT)
|
||||||
|
let jobName = 'MyPrintJob'
|
||||||
|
printHelper.printBitmap(jobName, img)
|
||||||
|
callback(true)
|
||||||
|
} catch (e) {
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,3 +555,15 @@ export const getTones = () => {
|
||||||
|
|
||||||
return { tones, defaultTone: defaultToneUri ? defaultTone : tones[0] }
|
return { tones, defaultTone: defaultToneUri ? defaultTone : tones[0] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//DETECT RTL LANGUAGE
|
||||||
|
export const RTL = (): boolean => {
|
||||||
|
const ctx = Utils.android.getApplicationContext()
|
||||||
|
const config = ctx.getResources().getConfiguration()
|
||||||
|
return config.getLayoutDirection() == android.view.View.LAYOUT_DIRECTION_RTL
|
||||||
|
}
|
||||||
|
|
||||||
|
//WAKE LOCK
|
||||||
|
export const wakeLock = (bool) => {
|
||||||
|
bool ? !wl.isHeld() && wl.acquire() : wl.isHeld() && wl.release()
|
||||||
|
}
|
||||||
|
|
343
app/store.ts
|
@ -5,11 +5,17 @@ import { openOrCreate } from '@akylas/nativescript-sqlite'
|
||||||
import {
|
import {
|
||||||
getFileAccess,
|
getFileAccess,
|
||||||
File,
|
File,
|
||||||
ApplicationSettings,
|
|
||||||
Application,
|
Application,
|
||||||
knownFolders,
|
knownFolders,
|
||||||
path,
|
path,
|
||||||
} from '@nativescript/core'
|
} from '@nativescript/core'
|
||||||
|
import {
|
||||||
|
getNumber,
|
||||||
|
setNumber,
|
||||||
|
getString,
|
||||||
|
setString,
|
||||||
|
} from '@nativescript/core/application-settings'
|
||||||
|
import * as utils from '~/shared/utils'
|
||||||
|
|
||||||
// OPEN DATABASE FILE
|
// OPEN DATABASE FILE
|
||||||
const db = openOrCreate(
|
const db = openOrCreate(
|
||||||
|
@ -31,6 +37,11 @@ db.execute(
|
||||||
'CREATE TABLE IF NOT EXISTS mealPlans (date INT, type TEXT, title TEXT)'
|
'CREATE TABLE IF NOT EXISTS mealPlans (date INT, type TEXT, title TEXT)'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CREATE timerPresets TABLE
|
||||||
|
db.execute(
|
||||||
|
'CREATE TABLE IF NOT EXISTS timerPresets (id INT PRIMARY KEY, label TEXT, time TEXT)'
|
||||||
|
)
|
||||||
|
|
||||||
const defaultCuisines = [
|
const defaultCuisines = [
|
||||||
'American',
|
'American',
|
||||||
'Brazilian',
|
'Brazilian',
|
||||||
|
@ -136,21 +147,22 @@ const defaultUnits = [
|
||||||
'medium',
|
'medium',
|
||||||
'large',
|
'large',
|
||||||
]
|
]
|
||||||
|
|
||||||
const listItems = {
|
const listItems = {
|
||||||
cuisines: {
|
cuisines: {
|
||||||
sort: true,
|
sort: 1,
|
||||||
defaultItems: defaultCuisines,
|
defaultItems: defaultCuisines,
|
||||||
},
|
},
|
||||||
categories: {
|
categories: {
|
||||||
sort: true,
|
sort: 1,
|
||||||
defaultItems: defaultCategories,
|
defaultItems: defaultCategories,
|
||||||
},
|
},
|
||||||
yieldUnits: {
|
yieldUnits: {
|
||||||
sort: false,
|
sort: 0,
|
||||||
defaultItems: defaultYieldUnits,
|
defaultItems: defaultYieldUnits,
|
||||||
},
|
},
|
||||||
units: {
|
units: {
|
||||||
sort: false,
|
sort: 0,
|
||||||
defaultItems: defaultUnits,
|
defaultItems: defaultUnits,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -227,10 +239,15 @@ export default new Vuex.Store({
|
||||||
trans: '\ue93c',
|
trans: '\ue93c',
|
||||||
delay: '\ue93d',
|
delay: '\ue93d',
|
||||||
ring: '\ue93e',
|
ring: '\ue93e',
|
||||||
|
print: '\ue93f',
|
||||||
},
|
},
|
||||||
currentComponent: 'EnRecipes',
|
currentComponent: 'EnRecipes',
|
||||||
sortType: 'Oldest first',
|
sortType: 'random',
|
||||||
language: [
|
language: [
|
||||||
|
{
|
||||||
|
locale: 'ar',
|
||||||
|
title: 'العربية',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
locale: 'da',
|
locale: 'da',
|
||||||
title: 'Dansk',
|
title: 'Dansk',
|
||||||
|
@ -239,10 +256,18 @@ export default new Vuex.Store({
|
||||||
locale: 'de',
|
locale: 'de',
|
||||||
title: 'Deutsch',
|
title: 'Deutsch',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
locale: 'en-IN',
|
||||||
|
title: 'English (IN)',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
locale: 'en-GB',
|
locale: 'en-GB',
|
||||||
title: 'English (UK)',
|
title: 'English (UK)',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
locale: 'en-US',
|
||||||
|
title: 'English (US)',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
locale: 'es',
|
locale: 'es',
|
||||||
title: 'Español',
|
title: 'Español',
|
||||||
|
@ -267,10 +292,6 @@ export default new Vuex.Store({
|
||||||
locale: 'hi',
|
locale: 'hi',
|
||||||
title: 'हिंदी',
|
title: 'हिंदी',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
locale: 'ml',
|
|
||||||
title: 'മലയാളം',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
locale: 'id',
|
locale: 'id',
|
||||||
title: 'Indonesia',
|
title: 'Indonesia',
|
||||||
|
@ -279,6 +300,14 @@ export default new Vuex.Store({
|
||||||
locale: 'it',
|
locale: 'it',
|
||||||
title: 'Italiano',
|
title: 'Italiano',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
locale: 'ja',
|
||||||
|
title: '日本語',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
locale: 'ml',
|
||||||
|
title: 'മലയാളം',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
locale: 'nb-NO',
|
locale: 'nb-NO',
|
||||||
title: 'Norsk bokmål',
|
title: 'Norsk bokmål',
|
||||||
|
@ -287,10 +316,10 @@ export default new Vuex.Store({
|
||||||
locale: 'nl',
|
locale: 'nl',
|
||||||
title: 'Nederlands',
|
title: 'Nederlands',
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// locale: 'pt',
|
locale: 'pt',
|
||||||
// title: 'Português',
|
title: 'Português',
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
locale: 'pt-BR',
|
locale: 'pt-BR',
|
||||||
title: 'Português (BR)',
|
title: 'Português (BR)',
|
||||||
|
@ -308,97 +337,114 @@ export default new Vuex.Store({
|
||||||
title: 'తెలుగు',
|
title: 'తెలుగు',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
shakeEnabled: ApplicationSettings.getBoolean('shakeEnabled', true),
|
shakeEnabled: getNumber('shakeEnabled', 1),
|
||||||
importSummary: {
|
importSummary: {
|
||||||
found: 0,
|
found: 0,
|
||||||
imported: 0,
|
imported: 0,
|
||||||
updated: 0,
|
updated: 0,
|
||||||
},
|
},
|
||||||
layout: ApplicationSettings.getString('layout', 'detailed'),
|
layout: getString('layout', 'detailed'),
|
||||||
selectedCuisine: null,
|
selectedCuisine: null,
|
||||||
selectedCategory: null,
|
selectedCategory: null,
|
||||||
selectedTag: null,
|
selectedTag: null,
|
||||||
appTheme: 'sysDef',
|
appTheme: 'sysDef',
|
||||||
mondayFirst: ApplicationSettings.getBoolean('mondayFirst', false),
|
mondayFirst: getNumber('mondayFirst', 0),
|
||||||
timerDelay: ApplicationSettings.getString('timerDelay', '1 minute'),
|
timerDelay: getString('timerDelay', '1 minute'),
|
||||||
timerSound: {},
|
timerSound: {},
|
||||||
timerVibrate: ApplicationSettings.getBoolean('timerVibrate', false),
|
timerVibrate: getNumber('timerVibrate', 0),
|
||||||
timerPresets: [
|
timerPresets: [],
|
||||||
{
|
|
||||||
id: 534534563,
|
|
||||||
label: 'Soft Eggs',
|
|
||||||
recipeID: null,
|
|
||||||
time: '00:06:00',
|
|
||||||
timerInterval: null,
|
|
||||||
isPaused: false,
|
|
||||||
preset: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 564646,
|
|
||||||
label: 'Medium Eggs',
|
|
||||||
recipeID: null,
|
|
||||||
time: '00:08:00',
|
|
||||||
timerInterval: null,
|
|
||||||
isPaused: false,
|
|
||||||
preset: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 43276767,
|
|
||||||
label: 'Hard Eggs',
|
|
||||||
recipeID: null,
|
|
||||||
time: '00:10:00',
|
|
||||||
timerInterval: null,
|
|
||||||
isPaused: false,
|
|
||||||
preset: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
activeTimers: [],
|
activeTimers: [],
|
||||||
timerIntervals: [],
|
FGService: 0,
|
||||||
|
RTL: 0,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
addTimerPreset(state, timer) {
|
setRTL(state) {
|
||||||
state.timerPresets.push(timer)
|
state.RTL = utils.RTL() as any | 0
|
||||||
},
|
},
|
||||||
removeTimerPreset(state, index) {
|
setFGService(state, val) {
|
||||||
state.timerPresets.splice(index, 1)
|
state.FGService = val
|
||||||
|
},
|
||||||
|
addTimerPreset(state, timer) {
|
||||||
|
let i = state.timerPresets.findIndex((e) => e.id == timer.id)
|
||||||
|
if (state.timerPresets.some((e) => e.id == timer.id)) {
|
||||||
|
state.timerPresets.splice(i, 1, timer)
|
||||||
|
} else state.timerPresets.push(timer)
|
||||||
|
db.execute(
|
||||||
|
`REPLACE INTO timerPresets (id, label, time) VALUES (?, ?, ?)`,
|
||||||
|
[timer.id, timer.label, timer.time]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
deleteTimerPreset(state, i) {
|
||||||
|
let id = state.timerPresets[i]
|
||||||
|
state.timerPresets.splice(i, 1)
|
||||||
|
db.execute(`DELETE FROM timerPresets WHERE id = ${id}`)
|
||||||
|
},
|
||||||
|
initTimerPresets(state) {
|
||||||
|
if (!state.timerPresets.length)
|
||||||
|
db.select(`SELECT * FROM timerPresets`).then((res) => {
|
||||||
|
res.forEach((t) => {
|
||||||
|
t.recipeID = 0
|
||||||
|
t.timerInt = 0
|
||||||
|
t.isPaused = 0
|
||||||
|
t.preset = 1
|
||||||
|
t.done = 0
|
||||||
|
state.timerPresets.push(t)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
importTimerPresets(state, timers) {
|
||||||
|
let newPresets = timers.filter(
|
||||||
|
(e) => !state.timerPresets.some((f) => f.id === e.id)
|
||||||
|
)
|
||||||
|
newPresets.forEach((t) => {
|
||||||
|
db.execute(
|
||||||
|
`INSERT INTO timerPresets (id, label, time) VALUES (?, ?, ?)`,
|
||||||
|
[t.id, t.label, t.time]
|
||||||
|
)
|
||||||
|
state.timerPresets.push(t)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
clearTimerInterval(state) {
|
clearTimerInterval(state) {
|
||||||
state.activeTimers.forEach((e) => {
|
state.activeTimers.forEach((e) => {
|
||||||
clearInterval(e.timerInterval)
|
clearInterval(e.timerInt)
|
||||||
e.timerInterval = null
|
e.timerInt = 0
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
addActiveTimer(state, { timer, index }) {
|
addActiveTimer(state, { timer, i }) {
|
||||||
state.activeTimers.splice(index, 0, timer)
|
state.activeTimers.splice(i, 0, timer)
|
||||||
|
},
|
||||||
|
sortActiveTimers(state) {
|
||||||
|
let a = state.activeTimers.reduce((acc, e) => {
|
||||||
|
;(acc[e.recipeID] = acc[e.recipeID] || []).push(e)
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
state.activeTimers = [...(<any>Object).values(a).flat(2)]
|
||||||
},
|
},
|
||||||
updateActiveTimer(state, timer) {
|
updateActiveTimer(state, timer) {
|
||||||
let index = state.activeTimers.findIndex((e) => e.id == timer.id)
|
let i = state.activeTimers.findIndex((e) => e.id == timer.id)
|
||||||
state.activeTimers.splice(index, 1, timer)
|
state.activeTimers.splice(i, 1, timer)
|
||||||
},
|
},
|
||||||
removeActiveTimer(state, index) {
|
removeActiveTimer(state, i) {
|
||||||
state.activeTimers.splice(index, 1)
|
state.activeTimers.splice(i, 1)
|
||||||
},
|
},
|
||||||
setTimerDelay(state, delay) {
|
setTimerDelay(state, delay) {
|
||||||
state.timerDelay = delay
|
state.timerDelay = delay
|
||||||
ApplicationSettings.setString('timerDelay', delay)
|
setString('timerDelay', delay)
|
||||||
},
|
},
|
||||||
setTimerSound(state, sound) {
|
setTimerSound(state, sound) {
|
||||||
state.timerSound = {
|
state.timerSound = sound
|
||||||
title: sound.title,
|
setString('timerSound', JSON.stringify(sound))
|
||||||
uri: sound.uri,
|
|
||||||
}
|
|
||||||
ApplicationSettings.setString('timerSound', JSON.stringify(sound))
|
|
||||||
},
|
},
|
||||||
setTimerVibrate(state, bool) {
|
setTimerVibrate(state, n) {
|
||||||
state.timerVibrate = bool
|
state.timerVibrate = n
|
||||||
ApplicationSettings.setBoolean('timerVibrate', bool)
|
setNumber('timerVibrate', n)
|
||||||
},
|
},
|
||||||
clearImportSummary(state) {
|
clearImportSummary(state) {
|
||||||
for (const key in state.importSummary) state.importSummary[key] = 0
|
for (const key in state.importSummary) state.importSummary[key] = 0
|
||||||
},
|
},
|
||||||
setFirstDay(state, bool) {
|
setFirstDay(state, n) {
|
||||||
state.mondayFirst = bool
|
state.mondayFirst = n | 0
|
||||||
ApplicationSettings.setBoolean('mondayFirst', bool)
|
setNumber('mondayFirst', n | 0)
|
||||||
},
|
},
|
||||||
setTheme(state, theme) {
|
setTheme(state, theme) {
|
||||||
switch (theme) {
|
switch (theme) {
|
||||||
|
@ -414,7 +460,7 @@ export default new Vuex.Store({
|
||||||
state.appTheme = theme
|
state.appTheme = theme
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
ApplicationSettings.setString('appTheme', theme)
|
setString('appTheme', theme)
|
||||||
},
|
},
|
||||||
clearFilter(state) {
|
clearFilter(state) {
|
||||||
state.selectedCuisine = state.selectedCategory = state.selectedTag = null
|
state.selectedCuisine = state.selectedCategory = state.selectedTag = null
|
||||||
|
@ -436,8 +482,8 @@ export default new Vuex.Store({
|
||||||
state.recipes.push(e)
|
state.recipes.push(e)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
state.shakeEnabled = ApplicationSettings.getBoolean('shakeEnabled', true)
|
state.shakeEnabled = getNumber('shakeEnabled', 1)
|
||||||
state.sortType = ApplicationSettings.getString('sortType', 'Oldest first')
|
state.sortType = getString('sortType', 'random')
|
||||||
},
|
},
|
||||||
importRecipesFromJSON(state, recipes) {
|
importRecipesFromJSON(state, recipes) {
|
||||||
let localRecipesIDs, partition
|
let localRecipesIDs, partition
|
||||||
|
@ -463,6 +509,12 @@ export default new Vuex.Store({
|
||||||
r.yieldQuantity = r.yield.quantity
|
r.yieldQuantity = r.yield.quantity
|
||||||
r.yieldUnit = r.yield.unit
|
r.yieldUnit = r.yield.unit
|
||||||
delete r.yield
|
delete r.yield
|
||||||
|
function getTime(d) {
|
||||||
|
return new Date(d).getTime()
|
||||||
|
}
|
||||||
|
r.lastTried = getTime(r.lastTried)
|
||||||
|
r.lastModified = getTime(r.lastModified)
|
||||||
|
r.created = getTime(r.created)
|
||||||
return r
|
return r
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -492,9 +544,9 @@ export default new Vuex.Store({
|
||||||
JSON.stringify(r.notes),
|
JSON.stringify(r.notes),
|
||||||
r.favorite ? 1 : 0,
|
r.favorite ? 1 : 0,
|
||||||
r.tried ? 1 : 0,
|
r.tried ? 1 : 0,
|
||||||
r.lastTried ? new Date(r.lastTried).getTime() : null,
|
r.lastTried,
|
||||||
r.lastModified ? new Date(r.lastModified).getTime() : null,
|
r.lastModified,
|
||||||
r.created ? new Date(r.created).getTime() : null,
|
r.created,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -533,9 +585,9 @@ export default new Vuex.Store({
|
||||||
JSON.stringify(r.notes),
|
JSON.stringify(r.notes),
|
||||||
r.favorite ? 1 : 0,
|
r.favorite ? 1 : 0,
|
||||||
r.tried ? 1 : 0,
|
r.tried ? 1 : 0,
|
||||||
r.lastTried ? new Date(r.lastTried).getTime() : null,
|
r.lastTried,
|
||||||
r.lastModified ? new Date(r.lastModified).getTime() : null,
|
r.lastModified,
|
||||||
r.created ? new Date(r.created).getTime() : null,
|
r.created,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -649,9 +701,9 @@ export default new Vuex.Store({
|
||||||
function exist({ id }, index: number) {
|
function exist({ id }, index: number) {
|
||||||
if (id === recipe.id) {
|
if (id === recipe.id) {
|
||||||
i = index
|
i = index
|
||||||
return true
|
return 1
|
||||||
}
|
}
|
||||||
return false
|
return 0
|
||||||
}
|
}
|
||||||
state.recipes.some(exist)
|
state.recipes.some(exist)
|
||||||
? Object.assign(state.recipes[i], recipe)
|
? Object.assign(state.recipes[i], recipe)
|
||||||
|
@ -659,9 +711,9 @@ export default new Vuex.Store({
|
||||||
},
|
},
|
||||||
deleteRecipes(state, ids) {
|
deleteRecipes(state, ids) {
|
||||||
ids.forEach((id: string) => {
|
ids.forEach((id: string) => {
|
||||||
let index = state.recipes.findIndex((e) => e.id === id)
|
let i = state.recipes.findIndex((e) => e.id === id)
|
||||||
getFileAccess().deleteFile(state.recipes[index].image)
|
getFileAccess().deleteFile(state.recipes[i].image)
|
||||||
state.recipes.splice(index, 1)
|
state.recipes.splice(i, 1)
|
||||||
db.execute(`DELETE FROM recipes WHERE id = '${id}'`)
|
db.execute(`DELETE FROM recipes WHERE id = '${id}'`)
|
||||||
state.recipes.forEach((e, i) => {
|
state.recipes.forEach((e, i) => {
|
||||||
if (e.combinations.includes(id)) {
|
if (e.combinations.includes(id)) {
|
||||||
|
@ -774,11 +826,11 @@ export default new Vuex.Store({
|
||||||
type = 'snacks'
|
type = 'snacks'
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return f.title === e.title && f.date === date && f.type === type
|
return f.title == e.title && f.date == date && f.type == type
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return !state.mealPlans.some(
|
return !state.mealPlans.some(
|
||||||
(f) => f.title === e.title && f.date === e.date && f.type === e.type
|
(f) => f.title == e.title && f.date == e.date && f.type == e.type
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -835,47 +887,48 @@ export default new Vuex.Store({
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
addMealPlan(state, { date, type, title }) {
|
addMealPlan(state, { date, type, title, index, inDB }) {
|
||||||
let mealPlan = {
|
let mealPlan = {
|
||||||
date,
|
date,
|
||||||
type,
|
type,
|
||||||
title,
|
title,
|
||||||
}
|
}
|
||||||
state.mealPlans.push(mealPlan)
|
if (inDB) {
|
||||||
const cols = Object.keys(mealPlan).join(', ')
|
const cols = Object.keys(mealPlan).join(', ')
|
||||||
const placeholder = Object.keys(mealPlan)
|
const placeholder = Object.keys(mealPlan)
|
||||||
.fill('?')
|
.fill('?')
|
||||||
.join(', ')
|
.join(', ')
|
||||||
db.execute(
|
|
||||||
`INSERT INTO mealPlans (${cols}) VALUES (${placeholder})`,
|
|
||||||
Object.values(mealPlan)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
deleteMealPlan(state, { date, type, title }) {
|
|
||||||
let index = state.mealPlans.findIndex(
|
|
||||||
(e) => e.title === title && e.type === type && e.date === date
|
|
||||||
)
|
|
||||||
state.mealPlans.splice(index, 1)
|
|
||||||
state.mealPlans = [...state.mealPlans]
|
|
||||||
db.execute(
|
|
||||||
`DELETE FROM mealPlans WHERE date = ${date} AND type = '${type}' AND title = '${title}'`
|
|
||||||
)
|
|
||||||
},
|
|
||||||
toggleState(state, { id, key, setDate }) {
|
|
||||||
let index = state.recipes.findIndex((e) => e.id == id)
|
|
||||||
state.recipes[index][key] = state.recipes[index][key] ? 0 : 1
|
|
||||||
db.execute(
|
|
||||||
`UPDATE recipes SET ${key} = ${state.recipes[index][key]} WHERE id = '${id}'`
|
|
||||||
)
|
|
||||||
if (setDate) {
|
|
||||||
state.recipes[index].lastTried = new Date().getTime()
|
|
||||||
db.execute(
|
db.execute(
|
||||||
`UPDATE recipes SET lastTried = ${state.recipes[index].lastTried} WHERE id = '${id}'`
|
`INSERT INTO mealPlans (${cols}) VALUES (${placeholder})`,
|
||||||
|
Object.values(mealPlan)
|
||||||
)
|
)
|
||||||
|
if (index === null) state.mealPlans.push(mealPlan)
|
||||||
|
} else {
|
||||||
|
state.mealPlans.splice(index, 0, mealPlan)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setComponent(state, comp) {
|
deleteMealPlan(state, { date, type, title, index, inDB }) {
|
||||||
state.currentComponent = comp
|
if (inDB) {
|
||||||
|
db.execute(
|
||||||
|
`DELETE FROM mealPlans WHERE date = ${date} AND type = '${type}' AND title = '${title}'`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
state.mealPlans.splice(index, 1)
|
||||||
|
state.mealPlans = [...state.mealPlans]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleState(state, { id, key, setDate }) {
|
||||||
|
let i = state.recipes.findIndex((e) => e.id == id)
|
||||||
|
state.recipes[i][key] = state.recipes[i][key] ? 0 : 1
|
||||||
|
db.execute(
|
||||||
|
`UPDATE recipes SET ${key} = ${state.recipes[i][key]} WHERE id = '${id}'`
|
||||||
|
)
|
||||||
|
if (setDate) {
|
||||||
|
state.recipes[i].lastTried = new Date().getTime()
|
||||||
|
db.execute(
|
||||||
|
`UPDATE recipes SET lastTried = ${state.recipes[i].lastTried} WHERE id = '${id}'`
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
unSyncCombinations(state, { id, combinations }) {
|
unSyncCombinations(state, { id, combinations }) {
|
||||||
state.recipes.forEach((e, i) => {
|
state.recipes.forEach((e, i) => {
|
||||||
|
@ -890,19 +943,17 @@ export default new Vuex.Store({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
setShake(state, shake) {
|
setShake(state, shake) {
|
||||||
state.shakeEnabled = shake
|
state.shakeEnabled = shake | 0
|
||||||
ApplicationSettings.setBoolean('shakeEnabled', shake)
|
setNumber('shakeEnabled', shake | 0)
|
||||||
},
|
},
|
||||||
setRating(state, { id, rating }) {
|
setRating(state, { id, rating }) {
|
||||||
let index = state.recipes.findIndex((e) => e.id == id)
|
let i = state.recipes.findIndex((e) => e.id == id)
|
||||||
state.recipes[index].rating = rating
|
state.recipes[i].rating = rating
|
||||||
db.execute(`UPDATE recipes SET rating = ${rating} WHERE id = '${id}'`)
|
db.execute(`UPDATE recipes SET rating = ${rating} WHERE id = '${id}'`)
|
||||||
},
|
},
|
||||||
toggleCart(state, { id }) {
|
toggleCart(state, { id }) {
|
||||||
let index = state.recipes.indexOf(
|
let i = state.recipes.indexOf(state.recipes.filter((e) => e.id === id)[0])
|
||||||
state.recipes.filter((e) => e.id === id)[0]
|
state.recipes[i].inBag = !state.recipes[i].inBag
|
||||||
)
|
|
||||||
state.recipes[index].inBag = !state.recipes[index].inBag
|
|
||||||
},
|
},
|
||||||
unlinkBrokenImages(state) {
|
unlinkBrokenImages(state) {
|
||||||
state.recipes.forEach((r, i) => {
|
state.recipes.forEach((r, i) => {
|
||||||
|
@ -924,11 +975,26 @@ export default new Vuex.Store({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
setRTL({ commit }) {
|
||||||
|
commit('setRTL')
|
||||||
|
},
|
||||||
|
sortActiveTimers({ commit }) {
|
||||||
|
commit('sortActiveTimers')
|
||||||
|
},
|
||||||
|
setFGService({ commit }, val) {
|
||||||
|
commit('setFGService', val)
|
||||||
|
},
|
||||||
addTimerPreset({ commit }, timer) {
|
addTimerPreset({ commit }, timer) {
|
||||||
commit('addTimerPreset', timer)
|
commit('addTimerPreset', timer)
|
||||||
},
|
},
|
||||||
removeTimerPreset({ commit }, timer) {
|
deleteTimerPreset({ commit }, timer) {
|
||||||
commit('removeTimerPreset', timer)
|
commit('deleteTimerPreset', timer)
|
||||||
|
},
|
||||||
|
initTimerPresets({ commit }) {
|
||||||
|
commit('initTimerPresets')
|
||||||
|
},
|
||||||
|
importTimerPresets({ commit }, timers) {
|
||||||
|
commit('importTimerPresets', timers)
|
||||||
},
|
},
|
||||||
clearTimerInterval({ commit }) {
|
clearTimerInterval({ commit }) {
|
||||||
commit('clearTimerInterval')
|
commit('clearTimerInterval')
|
||||||
|
@ -939,8 +1005,8 @@ export default new Vuex.Store({
|
||||||
updateActiveTimer({ commit }, timer) {
|
updateActiveTimer({ commit }, timer) {
|
||||||
commit('updateActiveTimer', timer)
|
commit('updateActiveTimer', timer)
|
||||||
},
|
},
|
||||||
removeActiveTimer({ commit }, index) {
|
removeActiveTimer({ commit }, i) {
|
||||||
commit('removeActiveTimer', index)
|
commit('removeActiveTimer', i)
|
||||||
},
|
},
|
||||||
setTimerDelay({ commit }, delay) {
|
setTimerDelay({ commit }, delay) {
|
||||||
commit('setTimerDelay', delay)
|
commit('setTimerDelay', delay)
|
||||||
|
@ -948,14 +1014,14 @@ export default new Vuex.Store({
|
||||||
setTimerSound({ commit }, sound) {
|
setTimerSound({ commit }, sound) {
|
||||||
commit('setTimerSound', sound)
|
commit('setTimerSound', sound)
|
||||||
},
|
},
|
||||||
setTimerVibrate({ commit }, bool) {
|
setTimerVibrate({ commit }, n) {
|
||||||
commit('setTimerVibrate', bool)
|
commit('setTimerVibrate', n)
|
||||||
},
|
},
|
||||||
clearImportSummary({ commit }) {
|
clearImportSummary({ commit }) {
|
||||||
commit('clearImportSummary')
|
commit('clearImportSummary')
|
||||||
},
|
},
|
||||||
setFirstDay({ commit }, bool) {
|
setFirstDay({ commit }, n) {
|
||||||
commit('setFirstDay', bool)
|
commit('setFirstDay', n)
|
||||||
},
|
},
|
||||||
setTheme({ commit }, theme) {
|
setTheme({ commit }, theme) {
|
||||||
commit('setTheme', theme)
|
commit('setTheme', theme)
|
||||||
|
@ -1017,9 +1083,6 @@ export default new Vuex.Store({
|
||||||
toggleStateAction({ commit }, toggledRecipe) {
|
toggleStateAction({ commit }, toggledRecipe) {
|
||||||
commit('toggleState', toggledRecipe)
|
commit('toggleState', toggledRecipe)
|
||||||
},
|
},
|
||||||
setComponent({ commit }, comp) {
|
|
||||||
commit('setComponent', comp)
|
|
||||||
},
|
|
||||||
unSyncCombinationsAction({ commit }, combinations) {
|
unSyncCombinationsAction({ commit }, combinations) {
|
||||||
commit('unSyncCombinations', combinations)
|
commit('unSyncCombinations', combinations)
|
||||||
},
|
},
|
||||||
|
|
1233
package-lock.json
generated
11
package.json
|
@ -20,7 +20,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@akylas/nativescript-sqlite": "^3.3.12",
|
"@akylas/nativescript-sqlite": "^3.3.12",
|
||||||
"@nativescript-community/ui-collectionview": "^4.0.29",
|
"@nativescript-community/ui-collectionview": "^4.0.29",
|
||||||
"@nativescript/core": "^8.0.5",
|
"@nativescript-rtl/ui": "^0.1.8",
|
||||||
|
"@nativescript/core": "^8.0.7",
|
||||||
"@nativescript/local-notifications": "^5.0.3",
|
"@nativescript/local-notifications": "^5.0.3",
|
||||||
"@nativescript/localize": "^5.0.4",
|
"@nativescript/localize": "^5.0.4",
|
||||||
"@triniwiz/nativescript-accelerometer": "^4.0.3",
|
"@triniwiz/nativescript-accelerometer": "^4.0.3",
|
||||||
|
@ -32,11 +33,11 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nativescript/android": "8.0.0",
|
"@nativescript/android": "8.0.0",
|
||||||
"@nativescript/types": "^8.0.1",
|
"@nativescript/types": "^8.0.1",
|
||||||
"@nativescript/webpack": "beta",
|
"@nativescript/webpack": "^5.0.0-beta.12",
|
||||||
"@types/node": "^15.3.0",
|
"@types/node": "^15.9.0",
|
||||||
"nativescript-vue-template-compiler": "~2.9.0",
|
"nativescript-vue-template-compiler": "~2.9.0",
|
||||||
"sass": "^1.32.13",
|
"sass": "^1.34.1",
|
||||||
"typescript": "^4.2.4",
|
"typescript": "^4.2.4",
|
||||||
"vue": "^2.6.12"
|
"vue": "^2.6.13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|