ui changes
This commit is contained in:
parent
921c16ded0
commit
1de949f4de
32 changed files with 4274 additions and 17762 deletions
88
app/app.scss
88
app/app.scss
|
@ -15,17 +15,18 @@ Page {
|
||||||
font-family: "Inter-Medium";
|
font-family: "Inter-Medium";
|
||||||
font-size: 14;
|
font-size: 14;
|
||||||
}
|
}
|
||||||
.tb {
|
|
||||||
font-family: "Inter-Bold";
|
|
||||||
}
|
|
||||||
.ico {
|
.ico {
|
||||||
font-family: "enrecipes";
|
font-family: "enrecipes";
|
||||||
font-size: 24;
|
font-size: 24;
|
||||||
vertical-alignment: center;
|
vertical-alignment: center;
|
||||||
&.sm {
|
&.sm {
|
||||||
font-size: 16;
|
font-size: 16;
|
||||||
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.tb {
|
||||||
|
font-family: "Inter-Bold";
|
||||||
|
}
|
||||||
.tac {
|
.tac {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -54,8 +55,7 @@ Page {
|
||||||
border-color: $gray2;
|
border-color: $gray2;
|
||||||
}
|
}
|
||||||
.appbar,
|
.appbar,
|
||||||
.modal,
|
.modal {
|
||||||
.topPlate {
|
|
||||||
background: $gray0;
|
background: $gray0;
|
||||||
}
|
}
|
||||||
.fieldLabel,
|
.fieldLabel,
|
||||||
|
@ -71,11 +71,7 @@ Page {
|
||||||
color: $gray1;
|
color: $gray1;
|
||||||
background: $gray9;
|
background: $gray9;
|
||||||
}
|
}
|
||||||
Progress {
|
.fab {
|
||||||
progress-background-color: $gray4;
|
|
||||||
}
|
|
||||||
.fab,
|
|
||||||
.hlMsg {
|
|
||||||
color: $gray1;
|
color: $gray1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,8 +91,7 @@ Page {
|
||||||
border-color: $gray7;
|
border-color: $gray7;
|
||||||
}
|
}
|
||||||
.appbar,
|
.appbar,
|
||||||
.modal,
|
.modal {
|
||||||
.topPlate {
|
|
||||||
color: $gray0;
|
color: $gray0;
|
||||||
background: $gray8;
|
background: $gray8;
|
||||||
}
|
}
|
||||||
|
@ -113,18 +108,15 @@ Page {
|
||||||
color: $gray9;
|
color: $gray9;
|
||||||
background: $gray1;
|
background: $gray1;
|
||||||
}
|
}
|
||||||
Progress {
|
.fab {
|
||||||
progress-background-color: $gray5;
|
|
||||||
}
|
|
||||||
.fab,
|
|
||||||
.hlMsg {
|
|
||||||
color: $gray9;
|
color: $gray9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.Black {
|
.Black {
|
||||||
color: $gray2;
|
color: $gray2;
|
||||||
background: $gray10;
|
background: $gray10;
|
||||||
Page {
|
Page,
|
||||||
|
.filters {
|
||||||
background: $gray10;
|
background: $gray10;
|
||||||
}
|
}
|
||||||
TextField,
|
TextField,
|
||||||
|
@ -136,8 +128,7 @@ Page {
|
||||||
border-color: $gray8;
|
border-color: $gray8;
|
||||||
}
|
}
|
||||||
.appbar,
|
.appbar,
|
||||||
.modal,
|
.modal {
|
||||||
.topPlate {
|
|
||||||
color: $gray1;
|
color: $gray1;
|
||||||
background: $gray9;
|
background: $gray9;
|
||||||
}
|
}
|
||||||
|
@ -154,11 +145,7 @@ Page {
|
||||||
color: $gray10;
|
color: $gray10;
|
||||||
background: $gray2;
|
background: $gray2;
|
||||||
}
|
}
|
||||||
Progress {
|
.fab {
|
||||||
progress-background-color: $gray5;
|
|
||||||
}
|
|
||||||
.fab,
|
|
||||||
.hlMsg {
|
|
||||||
color: $gray10;
|
color: $gray10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,7 +178,8 @@ TextView {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
Progress {
|
Progress {
|
||||||
progress-color: $orange;
|
margin: 16;
|
||||||
|
background-color: $gray5;
|
||||||
}
|
}
|
||||||
Switch {
|
Switch {
|
||||||
background-color: $orange;
|
background-color: $orange;
|
||||||
|
@ -200,7 +188,7 @@ Switch {
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// Side Drawer
|
// Side Drawer
|
||||||
.segment {
|
.segment {
|
||||||
border-radius: 8;
|
border-radius: 12;
|
||||||
margin: 0 4 0 0;
|
margin: 0 4 0 0;
|
||||||
padding: 0 12;
|
padding: 0 12;
|
||||||
.value {
|
.value {
|
||||||
|
@ -216,7 +204,7 @@ Button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
padding: 8;
|
padding: 8;
|
||||||
border-radius: 8;
|
border-radius: 12;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
&:active {
|
&:active {
|
||||||
|
@ -276,7 +264,7 @@ Button {
|
||||||
padding: 8 16;
|
padding: 8 16;
|
||||||
.recipeInfo {
|
.recipeInfo {
|
||||||
vertical-alignment: top;
|
vertical-alignment: top;
|
||||||
padding: 4 0 4 8;
|
padding: 2 0 2 8;
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
padding: 0 0 4;
|
padding: 0 0 4;
|
||||||
|
@ -318,7 +306,7 @@ Button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
.imgHolder {
|
.imgHolder {
|
||||||
border-radius: 8;
|
border-radius: 12;
|
||||||
}
|
}
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// SETTINGS
|
// SETTINGS
|
||||||
|
@ -328,6 +316,7 @@ Button {
|
||||||
}
|
}
|
||||||
.options-list {
|
.options-list {
|
||||||
.option {
|
.option {
|
||||||
|
vertical-align: center;
|
||||||
padding: 14 8;
|
padding: 14 8;
|
||||||
margin: 0 16;
|
margin: 0 16;
|
||||||
.ico {
|
.ico {
|
||||||
|
@ -353,7 +342,7 @@ Button {
|
||||||
.icon {
|
.icon {
|
||||||
margin: 16;
|
margin: 16;
|
||||||
background: $orange;
|
background: $orange;
|
||||||
border-radius: 99;
|
border-radius: 16;
|
||||||
horizontal-alignment: center;
|
horizontal-alignment: center;
|
||||||
}
|
}
|
||||||
.name {
|
.name {
|
||||||
|
@ -367,16 +356,21 @@ Button {
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// VIEW RECIPE
|
// VIEW RECIPE
|
||||||
.photo {
|
.photo {
|
||||||
border-radius: 8;
|
border-radius: 12;
|
||||||
margin: 24 16 0 0;
|
margin: 24 16 0 0;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
.photoviewer {
|
||||||
|
width: 96;
|
||||||
|
height: 96;
|
||||||
|
opacity: 0;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
.attribute {
|
.attribute {
|
||||||
margin: 8 16;
|
margin: 8 16;
|
||||||
.title {
|
.title {
|
||||||
margin-right: 8;
|
margin-right: 8;
|
||||||
font-size: 12;
|
font-size: 12;
|
||||||
color: $gray6;
|
|
||||||
}
|
}
|
||||||
.value {
|
.value {
|
||||||
@extend .tb;
|
@extend .tb;
|
||||||
|
@ -425,18 +419,19 @@ Button {
|
||||||
line-height: 4;
|
line-height: 4;
|
||||||
padding: 16 0;
|
padding: 16 0;
|
||||||
}
|
}
|
||||||
|
.dateInfo {
|
||||||
|
padding: 32 16 16;
|
||||||
|
font-size: 12;
|
||||||
|
line-height: 4;
|
||||||
|
}
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// APPBAR
|
// APPBAR
|
||||||
.appbar {
|
.appbar {
|
||||||
z-index: 99;
|
z-index: 32;
|
||||||
min-height: 56;
|
min-height: 56;
|
||||||
margin: 8;
|
margin: 8;
|
||||||
padding: 4;
|
padding: 4;
|
||||||
border-radius: 10;
|
border-radius: 16;
|
||||||
elevation: 16;
|
|
||||||
&.hlMsg {
|
|
||||||
background: $orange;
|
|
||||||
}
|
|
||||||
.title {
|
.title {
|
||||||
@extend .tb;
|
@extend .tb;
|
||||||
@extend .tw;
|
@extend .tw;
|
||||||
|
@ -451,7 +446,7 @@ Button {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
z-index: 98;
|
z-index: 24;
|
||||||
padding: 4;
|
padding: 4;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
horizontal-alignment: left;
|
horizontal-alignment: left;
|
||||||
|
@ -469,7 +464,7 @@ Button {
|
||||||
width: 48;
|
width: 48;
|
||||||
height: 48;
|
height: 48;
|
||||||
margin: 0 4 0 0;
|
margin: 0 4 0 0;
|
||||||
border-radius: 8;
|
border-radius: 12;
|
||||||
background: $orange;
|
background: $orange;
|
||||||
}
|
}
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
|
@ -482,10 +477,6 @@ Button {
|
||||||
margin: 32 0 16;
|
margin: 32 0 16;
|
||||||
}
|
}
|
||||||
.countdown {
|
.countdown {
|
||||||
background-color: transparent;
|
|
||||||
width: 48;
|
|
||||||
height: 48;
|
|
||||||
z-index: 0;
|
|
||||||
font-size: 17;
|
font-size: 17;
|
||||||
color: $orange;
|
color: $orange;
|
||||||
}
|
}
|
||||||
|
@ -507,7 +498,7 @@ Button {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.day {
|
.day {
|
||||||
border-radius: 8;
|
border-radius: 12;
|
||||||
}
|
}
|
||||||
.hasPlans {
|
.hasPlans {
|
||||||
color: $orange;
|
color: $orange;
|
||||||
|
@ -536,7 +527,7 @@ Button {
|
||||||
.modal {
|
.modal {
|
||||||
max-width: 320;
|
max-width: 320;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 10;
|
border-radius: 12;
|
||||||
margin: 72 0;
|
margin: 72 0;
|
||||||
.title {
|
.title {
|
||||||
@extend .tb;
|
@extend .tb;
|
||||||
|
@ -566,7 +557,7 @@ Button {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
.shareItem {
|
.shareItem {
|
||||||
border-radius: 8;
|
border-radius: 12;
|
||||||
margin: 0 8 8;
|
margin: 0 8 8;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
.ico {
|
.ico {
|
||||||
|
@ -624,6 +615,7 @@ ActivityIndicator {
|
||||||
}
|
}
|
||||||
@keyframes fade {
|
@keyframes fade {
|
||||||
0% {
|
0% {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@
|
||||||
for="recipe in getList"
|
for="recipe in getList"
|
||||||
@loaded="onListLoad"
|
@loaded="onListLoad"
|
||||||
:itemTemplateSelector="getLayout"
|
:itemTemplateSelector="getLayout"
|
||||||
:colWidth="layout == 'grid' ? '50%' : '100%'"
|
:colWidth="layout == 'grid' || layout == 'photogrid' ? '50%' : '100%'"
|
||||||
@scroll="!selectMode && onScroll($event)"
|
@scroll="!selectMode && onScroll($event)"
|
||||||
>
|
>
|
||||||
<v-template name="header">
|
<v-template name="header">
|
||||||
|
@ -31,20 +31,20 @@
|
||||||
v-for="(item, index) in topmenu"
|
v-for="(item, index) in topmenu"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="{
|
:class="{
|
||||||
select: currentComponent === item.component,
|
select: currentComponent === item.title,
|
||||||
}"
|
}"
|
||||||
@touch="touchSelector($event, item.component)"
|
@touch="touchSelector($event, item.title, item.title)"
|
||||||
>
|
>
|
||||||
<Label class="ico" :text="icon[item.icon]" />
|
<Label class="ico" :text="icon[item.icon]" />
|
||||||
<Label
|
<Label
|
||||||
class="value"
|
class="value"
|
||||||
v-if="getRecipeCount(item.title)"
|
:hidden="!getRecipeCount(item.title)"
|
||||||
:text="getRecipeCount(item.title)"
|
:text="getRecipeCount(item.title)"
|
||||||
col="1"
|
col="1"
|
||||||
/>
|
/>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
<GridLayout
|
<GridLayout
|
||||||
v-if="currentComponent === 'Filtered recipes'"
|
:hidden="currentComponent !== 'Filtered recipes'"
|
||||||
rows="48"
|
rows="48"
|
||||||
columns="auto, auto"
|
columns="auto, auto"
|
||||||
class="segment"
|
class="segment"
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
select: currentComponent === 'Filtered recipes',
|
select: currentComponent === 'Filtered recipes',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<Label class="ico" :text="icon.sort" />
|
<Label class="ico" :text="icon.filter" />
|
||||||
<Label
|
<Label
|
||||||
class="value"
|
class="value"
|
||||||
:text="getRecipeCount('filtered')"
|
:text="getRecipeCount('filtered')"
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
<GridLayout
|
<GridLayout
|
||||||
class="recipeItem"
|
class="recipeItem"
|
||||||
:class="getItemPos(recipe.id)"
|
:class="getItemPos(recipe.id)"
|
||||||
rows="96"
|
rows="auto"
|
||||||
columns="96, *"
|
columns="96, *"
|
||||||
ref="recipe"
|
ref="recipe"
|
||||||
@longPress="
|
@longPress="
|
||||||
|
@ -77,9 +77,10 @@
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
class="imgHolder"
|
class="imgHolder"
|
||||||
|
verticalAlignment="top"
|
||||||
v-if="recipe.imageSrc"
|
v-if="recipe.imageSrc"
|
||||||
:src="recipe.imageSrc"
|
:src="recipe.imageSrc"
|
||||||
stretch="aspectFit"
|
stretch="none"
|
||||||
decodeWidth="96"
|
decodeWidth="96"
|
||||||
decodeHeight="96"
|
decodeHeight="96"
|
||||||
loadMode="async"
|
loadMode="async"
|
||||||
|
@ -87,6 +88,7 @@
|
||||||
<Label
|
<Label
|
||||||
v-else
|
v-else
|
||||||
class="ico imgHolder"
|
class="ico imgHolder"
|
||||||
|
verticalAlignment="top"
|
||||||
@loaded="centerLabel"
|
@loaded="centerLabel"
|
||||||
width="96"
|
width="96"
|
||||||
height="96"
|
height="96"
|
||||||
|
@ -94,15 +96,15 @@
|
||||||
:text="icon.img"
|
:text="icon.img"
|
||||||
/>
|
/>
|
||||||
<StackLayout class="recipeInfo" col="1">
|
<StackLayout class="recipeInfo" col="1">
|
||||||
<Label :text="recipe.title" class="tb title" />
|
<Label :text="recipe.title" class="tb title tw" />
|
||||||
<StackLayout class="attributes" orientation="horizontal">
|
<StackLayout class="attributes" orientation="horizontal"
|
||||||
<Label class="ico sm" :text="icon.cuisine" />
|
><Label class="ico sm" :text="icon.cuisine" />
|
||||||
<Label class="attr" :text="recipe.cuisine | L" />
|
<Label class="attr" :text="recipe.cuisine | L" />
|
||||||
<Label class="ico sm" :text="icon.category" />
|
<Label class="ico sm" :text="icon.category" />
|
||||||
<Label class="attr" :text="recipe.category | L" />
|
<Label class="attr" :text="recipe.category | L" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
v-if="recipe.tags.length"
|
:hidden="!recipe.tags.length"
|
||||||
class="attributes"
|
class="attributes"
|
||||||
orientation="horizontal"
|
orientation="horizontal"
|
||||||
>
|
>
|
||||||
|
@ -158,7 +160,7 @@
|
||||||
:text="icon.img"
|
:text="icon.img"
|
||||||
/>
|
/>
|
||||||
<StackLayout class="recipeInfo" row="1">
|
<StackLayout class="recipeInfo" row="1">
|
||||||
<Label :text="recipe.title" class="tb title" />
|
<Label :text="recipe.title" class="tb title tw" />
|
||||||
<StackLayout class="attributes" orientation="horizontal">
|
<StackLayout class="attributes" orientation="horizontal">
|
||||||
<Label class="ico sm" :text="icon.cuisine" />
|
<Label class="ico sm" :text="icon.cuisine" />
|
||||||
<Label class="attr" :text="recipe.cuisine | L" />
|
<Label class="attr" :text="recipe.cuisine | L" />
|
||||||
|
@ -168,7 +170,7 @@
|
||||||
<Label class="attr" :text="recipe.category | L" />
|
<Label class="attr" :text="recipe.category | L" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
v-if="recipe.tags.length"
|
:hidden="!recipe.tags.length"
|
||||||
class="attributes"
|
class="attributes"
|
||||||
orientation="horizontal"
|
orientation="horizontal"
|
||||||
>
|
>
|
||||||
|
@ -178,6 +180,43 @@
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</v-template>
|
</v-template>
|
||||||
|
<v-template name="photogrid">
|
||||||
|
<GridLayout
|
||||||
|
class="recipeItem grid"
|
||||||
|
:class="getItemPos(recipe.id)"
|
||||||
|
rows="auto, auto"
|
||||||
|
columns="*"
|
||||||
|
ref="recipe"
|
||||||
|
@longPress="
|
||||||
|
selectMode ? viewRecipe(recipe.id) : addToSelection(recipe.id)
|
||||||
|
"
|
||||||
|
@tap="
|
||||||
|
selectMode ? addToSelection(recipe.id) : viewRecipe(recipe.id)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
class="imgHolder"
|
||||||
|
v-if="recipe.imageSrc"
|
||||||
|
:src="recipe.imageSrc"
|
||||||
|
stretch="aspectFit"
|
||||||
|
:decodeWidth="imgWidth"
|
||||||
|
:decodeHeight="imgWidth"
|
||||||
|
loadMode="async"
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
v-else
|
||||||
|
width="100%"
|
||||||
|
:height="imgWidth"
|
||||||
|
@loaded="centerLabel"
|
||||||
|
class="ico imgHolder"
|
||||||
|
:fontSize="imgWidth / 2"
|
||||||
|
:text="icon.img"
|
||||||
|
/>
|
||||||
|
<StackLayout class="recipeInfo" row="1">
|
||||||
|
<Label :text="recipe.title" class="tb title tw" />
|
||||||
|
</StackLayout>
|
||||||
|
</GridLayout>
|
||||||
|
</v-template>
|
||||||
<v-template name="simple">
|
<v-template name="simple">
|
||||||
<GridLayout
|
<GridLayout
|
||||||
class="recipeItem simple"
|
class="recipeItem simple"
|
||||||
|
@ -192,7 +231,7 @@
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<StackLayout class="recipeInfo">
|
<StackLayout class="recipeInfo">
|
||||||
<Label :text="recipe.title" class="tb title" />
|
<Label :text="recipe.title" class="tb title tw" />
|
||||||
<StackLayout class="attributes" orientation="horizontal">
|
<StackLayout class="attributes" orientation="horizontal">
|
||||||
<Label class="ico sm" :text="icon.cuisine" />
|
<Label class="ico sm" :text="icon.cuisine" />
|
||||||
<Label class="attr" :text="recipe.cuisine | L" />
|
<Label class="attr" :text="recipe.cuisine | L" />
|
||||||
|
@ -200,7 +239,7 @@
|
||||||
<Label class="attr" :text="recipe.category | L" />
|
<Label class="attr" :text="recipe.category | L" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
v-if="recipe.tags.length"
|
:hidden="!recipe.tags.length"
|
||||||
class="attributes"
|
class="attributes"
|
||||||
orientation="horizontal"
|
orientation="horizontal"
|
||||||
>
|
>
|
||||||
|
@ -224,7 +263,7 @@
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<StackLayout class="recipeInfo">
|
<StackLayout class="recipeInfo">
|
||||||
<Label :text="recipe.title" class="tb title" />
|
<Label :text="recipe.title" class="tb title tw" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</v-template>
|
</v-template>
|
||||||
|
@ -273,7 +312,7 @@
|
||||||
rows="auto"
|
rows="auto"
|
||||||
columns="auto"
|
columns="auto"
|
||||||
class="appbar toolbar"
|
class="appbar toolbar"
|
||||||
v-if="showTools"
|
:hidden="!showTools"
|
||||||
>
|
>
|
||||||
<GridLayout
|
<GridLayout
|
||||||
row="1"
|
row="1"
|
||||||
|
@ -319,41 +358,34 @@
|
||||||
@textChange="updateList($event.value)"
|
@textChange="updateList($event.value)"
|
||||||
/>
|
/>
|
||||||
<Label
|
<Label
|
||||||
v-if="selectMode"
|
:hidden="!selectMode"
|
||||||
class="title"
|
class="title"
|
||||||
:text="`${selection.length} ${$options.filters.L('sltd')}`"
|
:text="`${selection.length} ${$options.filters.L('sltd')}`"
|
||||||
col="1"
|
col="1"
|
||||||
/>
|
/>
|
||||||
<Button
|
<StackLayout
|
||||||
v-if="recipes.length && !selectMode && !showSearch"
|
|
||||||
class="ico"
|
|
||||||
:text="selectMode ? icon.export : icon.sear"
|
|
||||||
col="2"
|
col="2"
|
||||||
|
colSpan="3"
|
||||||
|
orientation="horizontal"
|
||||||
|
:hidden="!recipes.length || selectMode || showSearch"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
class="ico"
|
||||||
|
:text="selectMode ? icon.exp : icon.sear"
|
||||||
@tap="selectMode ? exportSelection() : openSearch()"
|
@tap="selectMode ? exportSelection() : openSearch()"
|
||||||
/>
|
/>
|
||||||
|
<Button class="ico" :text="icon.sort" @tap="openSort" />
|
||||||
|
<Button class="ico" :text="icon.filter" @tap="openFilters" />
|
||||||
|
</StackLayout>
|
||||||
<Button
|
<Button
|
||||||
v-if="recipes.length && !showSearch && !selectMode"
|
:hidden="showSearch || selectMode"
|
||||||
class="ico"
|
|
||||||
:text="icon.sort"
|
|
||||||
col="3"
|
|
||||||
@tap="openSort"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
v-if="recipes.length && !showSearch && !selectMode"
|
|
||||||
class="ico"
|
|
||||||
:text="icon.sort"
|
|
||||||
col="4"
|
|
||||||
@tap="openFilters"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
v-if="!showSearch && !selectMode"
|
|
||||||
class="ico fab"
|
class="ico fab"
|
||||||
:text="icon.plus"
|
:text="icon.plus"
|
||||||
col="5"
|
col="5"
|
||||||
@tap="addRecipe()"
|
@tap="addRecipe"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
v-if="selectMode"
|
:hidden="!selectMode"
|
||||||
class="ico"
|
class="ico"
|
||||||
:text="icon.del"
|
:text="icon.del"
|
||||||
col="5"
|
col="5"
|
||||||
|
@ -375,6 +407,7 @@ import {
|
||||||
Device,
|
Device,
|
||||||
Screen,
|
Screen,
|
||||||
Color,
|
Color,
|
||||||
|
CoreTypes,
|
||||||
} from "@nativescript/core";
|
} from "@nativescript/core";
|
||||||
import { localize } from "@nativescript/localize";
|
import { localize } from "@nativescript/localize";
|
||||||
import {
|
import {
|
||||||
|
@ -390,7 +423,7 @@ import Settings from "./Settings";
|
||||||
import ActionDialog from "./modal/ActionDialog.vue";
|
import ActionDialog from "./modal/ActionDialog.vue";
|
||||||
import ConfirmDialog from "./modal/ConfirmDialog.vue";
|
import ConfirmDialog from "./modal/ConfirmDialog.vue";
|
||||||
import Filters from "./modal/Filters.vue";
|
import Filters from "./modal/Filters.vue";
|
||||||
import * as utils from "~/shared/utils.js";
|
import * as utils from "~/shared/utils";
|
||||||
let lastTime = 0;
|
let lastTime = 0;
|
||||||
let lastShake = 0;
|
let lastShake = 0;
|
||||||
let lastForce = 0;
|
let lastForce = 0;
|
||||||
|
@ -416,17 +449,14 @@ export default {
|
||||||
topmenu: [
|
topmenu: [
|
||||||
{
|
{
|
||||||
title: "EnRecipes",
|
title: "EnRecipes",
|
||||||
component: "EnRecipes",
|
|
||||||
icon: "home",
|
icon: "home",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "trylater",
|
title: "trylater",
|
||||||
component: "Try Later",
|
|
||||||
icon: "try",
|
icon: "try",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "favourites",
|
title: "favourites",
|
||||||
component: "Favourites",
|
|
||||||
icon: "fav",
|
icon: "fav",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -519,13 +549,13 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions([
|
...mapActions([
|
||||||
"setComponent",
|
"setComponent",
|
||||||
"setSortTypeAction",
|
|
||||||
"initListItems",
|
"initListItems",
|
||||||
"initRecipes",
|
"initRecipes",
|
||||||
"initMealPlans",
|
"initMealPlans",
|
||||||
"setShakeAction",
|
"setShake",
|
||||||
|
"setFirstDay",
|
||||||
"setLayout",
|
"setLayout",
|
||||||
"setSortTypeAction",
|
"setSortType",
|
||||||
"deleteRecipeAction",
|
"deleteRecipeAction",
|
||||||
"deleteRecipesAction",
|
"deleteRecipesAction",
|
||||||
"clearFilter",
|
"clearFilter",
|
||||||
|
@ -536,6 +566,9 @@ export default {
|
||||||
const window = Application.android.startActivity.getWindow();
|
const window = Application.android.startActivity.getWindow();
|
||||||
const decorView = window.getDecorView();
|
const decorView = window.getDecorView();
|
||||||
let sdkv = Device.sdkVersion;
|
let sdkv = Device.sdkVersion;
|
||||||
|
this.appTheme == "Light"
|
||||||
|
? decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)
|
||||||
|
: decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_DARK_STATUS_BAR);
|
||||||
function setColors(color) {
|
function setColors(color) {
|
||||||
window.setStatusBarColor(new Color(color).android);
|
window.setStatusBarColor(new Color(color).android);
|
||||||
sdkv >= 27 && window.setNavigationBarColor(new Color(color).android);
|
sdkv >= 27 && window.setNavigationBarColor(new Color(color).android);
|
||||||
|
@ -551,11 +584,8 @@ export default {
|
||||||
setColors("#000000");
|
setColors("#000000");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
sdkv >= 23 && this.appTheme == "Light"
|
if (sdkv >= 27)
|
||||||
? decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)
|
this.appTheme == "Light"
|
||||||
: decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_DARK_STATUS_BAR);
|
|
||||||
|
|
||||||
sdkv >= 27 && this.appTheme == "Light"
|
|
||||||
? decorView.setSystemUiVisibility(
|
? decorView.setSystemUiVisibility(
|
||||||
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||||
)
|
)
|
||||||
|
@ -567,16 +597,16 @@ export default {
|
||||||
const page = args.object;
|
const page = args.object;
|
||||||
page.bindingContext = new Observable();
|
page.bindingContext = new Observable();
|
||||||
this.filterFavourites
|
this.filterFavourites
|
||||||
? this.setComponent("Favourites")
|
? this.setComponent("favourites")
|
||||||
: this.filterTrylater
|
: this.filterTrylater
|
||||||
? this.setComponent("Try Later")
|
? this.setComponent("trylater")
|
||||||
: this.selectedCuisine
|
: this.selectedCuisine
|
||||||
? this.setComponent("Filtered recipes")
|
? this.setComponent("Filtered recipes")
|
||||||
: this.setComponent("EnRecipes");
|
: this.setComponent("EnRecipes");
|
||||||
if (this.shakeEnabled) {
|
if (this.shakeEnabled) {
|
||||||
if (utils.hasAccelerometer())
|
if (utils.hasAccelerometer())
|
||||||
startAccelerometerUpdates((data) => this.onSensorData(data));
|
startAccelerometerUpdates((data) => this.onSensorData(data));
|
||||||
else this.setShakeAction(false);
|
else this.setShake(false);
|
||||||
}
|
}
|
||||||
this.hijackBackEvent();
|
this.hijackBackEvent();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -606,22 +636,25 @@ export default {
|
||||||
this.scrollPos = Math.abs(y);
|
this.scrollPos = Math.abs(y);
|
||||||
let ab = this.appbar.translateY;
|
let ab = this.appbar.translateY;
|
||||||
if (!scrollUp && ab == 0) {
|
if (!scrollUp && ab == 0) {
|
||||||
this.animateInOut(
|
this.appbar.animate({
|
||||||
250,
|
translate: { x: 0, y: 64 },
|
||||||
false,
|
duration: 250,
|
||||||
(val) => (this.appbar.translateY = val * 64)
|
curve: CoreTypes.AnimationCurve.ease,
|
||||||
);
|
});
|
||||||
} else if (scrollUp && ab == 64) {
|
} else if (scrollUp && ab == 64) {
|
||||||
this.animateInOut(
|
this.appbar.animate({
|
||||||
250,
|
translate: { x: 0, y: 0 },
|
||||||
true,
|
duration: 250,
|
||||||
(val) => (this.appbar.translateY = val * 64)
|
curve: CoreTypes.AnimationCurve.ease,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getSpanSize(index) {
|
getSpanSize(index) {
|
||||||
return this.layout == "grid" && (index == 0 || index == 1) ? 2 : 1;
|
return (this.layout == "grid" || this.layout == "photogrid") &&
|
||||||
|
(index == 0 || index == 1)
|
||||||
|
? 2
|
||||||
|
: 1;
|
||||||
},
|
},
|
||||||
getLayout(args, index, items) {
|
getLayout(args, index, items) {
|
||||||
return index == 0 ? "header" : index == 1 ? "lists" : this.layout;
|
return index == 0 ? "header" : index == 1 ? "lists" : this.layout;
|
||||||
|
@ -646,7 +679,7 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
title: "srt",
|
title: "srt",
|
||||||
list: [
|
list: [
|
||||||
"Title",
|
"title",
|
||||||
"Rating",
|
"Rating",
|
||||||
"Quickest first",
|
"Quickest first",
|
||||||
"Slowest first",
|
"Slowest first",
|
||||||
|
@ -658,7 +691,7 @@ export default {
|
||||||
},
|
},
|
||||||
}).then((action) => {
|
}).then((action) => {
|
||||||
if (action && action !== "Cancel" && this.sortType !== action) {
|
if (action && action !== "Cancel" && this.sortType !== action) {
|
||||||
this.setSortTypeAction(action);
|
this.setSortType(action);
|
||||||
ApplicationSettings.setString("sortType", action);
|
ApplicationSettings.setString("sortType", action);
|
||||||
this.updateSort();
|
this.updateSort();
|
||||||
}
|
}
|
||||||
|
@ -668,6 +701,8 @@ export default {
|
||||||
|
|
||||||
//FILTER
|
//FILTER
|
||||||
openFilters() {
|
openFilters() {
|
||||||
|
this.setComponent("EnRecipes");
|
||||||
|
this.filterFavourites = this.filterTrylater = false;
|
||||||
this.showTools = false;
|
this.showTools = false;
|
||||||
this.releaseBackEvent();
|
this.releaseBackEvent();
|
||||||
this.$showModal(Filters).then(() => this.hijackBackEvent());
|
this.$showModal(Filters).then(() => this.hijackBackEvent());
|
||||||
|
@ -917,35 +952,27 @@ export default {
|
||||||
let dl1 = difficultyLevel(a.difficulty);
|
let dl1 = difficultyLevel(a.difficulty);
|
||||||
let dl2 = difficultyLevel(b.difficulty);
|
let dl2 = difficultyLevel(b.difficulty);
|
||||||
switch (this.sortType) {
|
switch (this.sortType) {
|
||||||
case "Title":
|
case "title":
|
||||||
return titleOrder > 0 ? 1 : titleOrder < 0 ? -1 : 0;
|
return titleOrder > 0 ? 1 : titleOrder < 0 ? -1 : 0;
|
||||||
break;
|
|
||||||
case "Quickest first":
|
case "Quickest first":
|
||||||
return d1 > d2 ? 1 : d1 < d2 ? -1 : 0;
|
return d1 > d2 ? 1 : d1 < d2 ? -1 : 0;
|
||||||
break;
|
|
||||||
case "Slowest first":
|
case "Slowest first":
|
||||||
return d1 > d2 ? -1 : d1 < d2 ? 1 : 0;
|
return d1 > d2 ? -1 : d1 < d2 ? 1 : 0;
|
||||||
break;
|
|
||||||
case "Rating":
|
case "Rating":
|
||||||
return r1 > r2 ? -1 : r1 < r2 ? 1 : 0;
|
return r1 > r2 ? -1 : r1 < r2 ? 1 : 0;
|
||||||
break;
|
|
||||||
case "Difficulty level":
|
case "Difficulty level":
|
||||||
return dl1 > dl2 ? 1 : dl1 < dl2 ? -1 : 0;
|
return dl1 > dl2 ? 1 : dl1 < dl2 ? -1 : 0;
|
||||||
break;
|
|
||||||
case "Last updated":
|
case "Last updated":
|
||||||
return ld1 < ld2 ? 1 : ld1 > ld2 ? -1 : 0;
|
return ld1 < ld2 ? 1 : ld1 > ld2 ? -1 : 0;
|
||||||
break;
|
|
||||||
case "Newest first":
|
case "Newest first":
|
||||||
return cd1 < cd2 ? 1 : cd1 > cd2 ? -1 : 0;
|
return cd1 < cd2 ? 1 : cd1 > cd2 ? -1 : 0;
|
||||||
break;
|
|
||||||
case "Oldest first":
|
case "Oldest first":
|
||||||
return cd1 < cd2 ? -1 : cd1 > cd2 ? 1 : 0;
|
return cd1 < cd2 ? -1 : cd1 > cd2 ? 1 : 0;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getItemPos(id) {
|
getItemPos(id) {
|
||||||
let length = this.filteredRecipes.length;
|
let length = this.filteredRecipes.length;
|
||||||
let l2 = this.layout == "grid";
|
let l2 = this.layout == "grid" || this.layout == "photogrid";
|
||||||
let oddOrEven = this.oddOrEven(id);
|
let oddOrEven = this.oddOrEven(id);
|
||||||
let itemPos =
|
let itemPos =
|
||||||
id == this.filteredRecipes[0].id ||
|
id == this.filteredRecipes[0].id ||
|
||||||
|
@ -995,7 +1022,7 @@ export default {
|
||||||
args.cancel = true;
|
args.cancel = true;
|
||||||
this.clearSelection();
|
this.clearSelection();
|
||||||
} else if (
|
} else if (
|
||||||
["Favourites", "Try Later", "Filtered recipes"].includes(
|
["favourites", "trylater", "Filtered recipes"].includes(
|
||||||
this.currentComponent
|
this.currentComponent
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
@ -1019,9 +1046,9 @@ export default {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (title !== this.currentComponent) {
|
} else if (title !== this.currentComponent) {
|
||||||
this.setComponent(to);
|
this.setComponent(title);
|
||||||
this.filterFavourites = to == "Favourites";
|
this.filterFavourites = to == "favourites";
|
||||||
this.filterTrylater = to == "Try Later";
|
this.filterTrylater = to == "trylater";
|
||||||
this.clearFilter();
|
this.clearFilter();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1051,11 +1078,9 @@ export default {
|
||||||
duration: 250,
|
duration: 250,
|
||||||
curve: "easeOut",
|
curve: "easeOut",
|
||||||
},
|
},
|
||||||
// backstackVisible: false,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
viewRandomRecipe() {
|
viewRandomRecipe() {
|
||||||
// this.showFAB =
|
|
||||||
this.showTools = false;
|
this.showTools = false;
|
||||||
this.$navigateTo(ViewRecipe, {
|
this.$navigateTo(ViewRecipe, {
|
||||||
props: {
|
props: {
|
||||||
|
@ -1067,15 +1092,14 @@ export default {
|
||||||
duration: 250,
|
duration: 250,
|
||||||
curve: "easeOut",
|
curve: "easeOut",
|
||||||
},
|
},
|
||||||
backstackVisible: false,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
touchSelector({ object, action }, comp) {
|
touchSelector({ object, action }, comp, title) {
|
||||||
let selected = this.currentComponent == comp;
|
let selected = this.currentComponent == comp;
|
||||||
object.className = action.match(/down|move/)
|
object.className = action.match(/down|move/)
|
||||||
? `segment ${selected ? "select" : "fade"}`
|
? `segment ${selected ? "select" : "fade"}`
|
||||||
: `segment ${selected && "select"}`;
|
: `segment ${selected && "select"}`;
|
||||||
if (action == "up") this.navigateTo(comp, comp);
|
if (action == "up") this.navigateTo(comp, title);
|
||||||
},
|
},
|
||||||
touchTool({ object, action }, comp, value) {
|
touchTool({ object, action }, comp, value) {
|
||||||
object.className = action.match(/down|move/) ? `tool fade` : `tool`;
|
object.className = action.match(/down|move/) ? `tool fade` : `tool`;
|
||||||
|
@ -1088,7 +1112,8 @@ export default {
|
||||||
if (!this.recipes.length) this.initRecipes();
|
if (!this.recipes.length) this.initRecipes();
|
||||||
this.initListItems();
|
this.initListItems();
|
||||||
if (!this.mealPlans.length) this.initMealPlans();
|
if (!this.mealPlans.length) this.initMealPlans();
|
||||||
this.setShakeAction(ApplicationSettings.getBoolean("shakeEnabled", true));
|
this.setShake(ApplicationSettings.getBoolean("shakeEnabled", true));
|
||||||
|
this.setFirstDay(ApplicationSettings.getBoolean("mondayFirst", false));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
class="dayName"
|
class="dayName"
|
||||||
row="1"
|
row="1"
|
||||||
:col="i"
|
:col="i"
|
||||||
v-for="(d, i) in dNames"
|
v-for="(d, i) in getDayNames"
|
||||||
:key="d"
|
:key="d"
|
||||||
:text="d | L"
|
:text="d | L"
|
||||||
/>
|
/>
|
||||||
|
@ -94,14 +94,14 @@
|
||||||
row="1"
|
row="1"
|
||||||
@loaded="onAppBarLoad"
|
@loaded="onAppBarLoad"
|
||||||
class="appbar"
|
class="appbar"
|
||||||
v-show="!showUndo"
|
:hidden="showUndo"
|
||||||
columns="auto, *, auto, auto"
|
columns="auto, *, auto, auto"
|
||||||
>
|
>
|
||||||
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
|
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
|
||||||
<Button
|
<Button
|
||||||
class="ico"
|
class="ico"
|
||||||
:text="icon.tod"
|
:text="icon.tod"
|
||||||
v-if="!isExactlyToday"
|
:hidden="isExactlyToday"
|
||||||
@tap="goToToday"
|
@tap="goToToday"
|
||||||
col="2"
|
col="2"
|
||||||
/>
|
/>
|
||||||
|
@ -115,19 +115,19 @@
|
||||||
<GridLayout
|
<GridLayout
|
||||||
row="1"
|
row="1"
|
||||||
class="appbar snackBar"
|
class="appbar snackBar"
|
||||||
v-show="showUndo"
|
:hidden="!showUndo"
|
||||||
columns="auto, *, auto"
|
columns="auto, *, auto"
|
||||||
>
|
>
|
||||||
<Button :text="countdown" class="countdown tb" />
|
<Button :text="countdown" class="ico countdown tb" />
|
||||||
<Label class="title" col="1" :text="snackMsg | L" />
|
<Label class="title" col="1" :text="snackMsg | L" />
|
||||||
<Button class="ico fab" :text="icon.alert" @tap="undoDel" col="3" />
|
<Button class="ico fab" :text="icon.undo" @tap="undoDel" col="3" />
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ApplicationSettings, Observable } from "@nativescript/core";
|
import { ApplicationSettings, Observable, CoreTypes } from "@nativescript/core";
|
||||||
import { mapState, mapActions } from "vuex";
|
import { mapState, mapActions } from "vuex";
|
||||||
import ViewRecipe from "./ViewRecipe.vue";
|
import ViewRecipe from "./ViewRecipe.vue";
|
||||||
import ActionDialogWithSearch from "./modal/ActionDialogWithSearch.vue";
|
import ActionDialogWithSearch from "./modal/ActionDialogWithSearch.vue";
|
||||||
|
@ -138,7 +138,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
appTheme: "Light",
|
appTheme: "Light",
|
||||||
mealTimes: ["breakfast", "lunch", "dinner", "snacks"],
|
mealTimes: ["breakfast", "lunch", "dinner", "snacks"],
|
||||||
dNames: ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"],
|
dNames: ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"],
|
||||||
year: 2021,
|
year: 2021,
|
||||||
mNames: [
|
mNames: [
|
||||||
"January",
|
"January",
|
||||||
|
@ -166,7 +166,7 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["icon", "recipes", "mealPlans"]),
|
...mapState(["icon", "recipes", "mealPlans", "mondayFirst"]),
|
||||||
todaysTime() {
|
todaysTime() {
|
||||||
return new Date(this.year, this.month, this.today, 0).getTime();
|
return new Date(this.year, this.month, this.today, 0).getTime();
|
||||||
},
|
},
|
||||||
|
@ -180,13 +180,16 @@ export default {
|
||||||
}, {});
|
}, {});
|
||||||
} else return 0;
|
} else return 0;
|
||||||
},
|
},
|
||||||
|
getDayNames() {
|
||||||
|
if (!this.mondayFirst) this.dNames.unshift(this.dNames.pop());
|
||||||
|
return this.dNames;
|
||||||
|
},
|
||||||
getCal() {
|
getCal() {
|
||||||
let y = this.year;
|
let y = this.year;
|
||||||
let m = this.month;
|
let m = this.month;
|
||||||
let t = this.today;
|
|
||||||
let d = new Date(y, m, t);
|
|
||||||
let ds = new Date(y, m + 1, 0).getDate();
|
let ds = new Date(y, m + 1, 0).getDate();
|
||||||
let fd = new Date(y, m, 1).getDay();
|
let fd = new Date(y, m, 1).getDay();
|
||||||
|
if (this.mondayFirst) fd -= 1;
|
||||||
let days = new Array(fd).fill(0);
|
let days = new Array(fd).fill(0);
|
||||||
for (let i = 1; i <= ds; i++) {
|
for (let i = 1; i <= ds; i++) {
|
||||||
days.push(i);
|
days.push(i);
|
||||||
|
@ -230,17 +233,17 @@ export default {
|
||||||
this.scrollPos = Math.abs(y);
|
this.scrollPos = Math.abs(y);
|
||||||
let ab = this.appbar.translateY;
|
let ab = this.appbar.translateY;
|
||||||
if (!scrollUp && ab == 0) {
|
if (!scrollUp && ab == 0) {
|
||||||
this.animateInOut(
|
this.appbar.animate({
|
||||||
250,
|
translate: { x: 0, y: 64 },
|
||||||
false,
|
duration: 250,
|
||||||
(val) => (this.appbar.translateY = val * 64)
|
curve: CoreTypes.AnimationCurve.ease,
|
||||||
);
|
});
|
||||||
} else if (scrollUp && ab == 64) {
|
} else if (scrollUp && ab == 64) {
|
||||||
this.animateInOut(
|
this.appbar.animate({
|
||||||
250,
|
translate: { x: 0, y: 0 },
|
||||||
true,
|
duration: 250,
|
||||||
(val) => (this.appbar.translateY = val * 64)
|
curve: CoreTypes.AnimationCurve.ease,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -275,7 +278,6 @@ export default {
|
||||||
duration: 250,
|
duration: 250,
|
||||||
curve: "easeOut",
|
curve: "easeOut",
|
||||||
},
|
},
|
||||||
// backstackVisible: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -362,11 +364,11 @@ export default {
|
||||||
},
|
},
|
||||||
showUndoBar(message) {
|
showUndoBar(message) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
clearTimeout(undoTimer);
|
||||||
this.showUndo = true;
|
this.showUndo = true;
|
||||||
this.snackMsg = message;
|
this.snackMsg = message;
|
||||||
this.countdown = 5;
|
this.countdown = 5;
|
||||||
let a = 5;
|
let a = 5;
|
||||||
clearTimeout(undoTimer);
|
|
||||||
undoTimer = setInterval(() => {
|
undoTimer = setInterval(() => {
|
||||||
if (this.undo) {
|
if (this.undo) {
|
||||||
this.showUndo = this.undo = false;
|
this.showUndo = this.undo = false;
|
||||||
|
|
|
@ -5,13 +5,12 @@
|
||||||
colSpan="2"
|
colSpan="2"
|
||||||
rowSpan="2"
|
rowSpan="2"
|
||||||
class="options-list"
|
class="options-list"
|
||||||
@loaded="listViewLoad"
|
|
||||||
for="item in items"
|
for="item in items"
|
||||||
>
|
>
|
||||||
<v-template if="$index == 0">
|
<v-template if="$index == 0">
|
||||||
<Label class="pageTitle" :text="'Settings' | L" />
|
<Label class="pageTitle" :text="'Settings' | L" />
|
||||||
</v-template>
|
</v-template>
|
||||||
<v-template if="$index == 7">
|
<v-template if="$index == 6">
|
||||||
<StackLayout class="listSpace"> </StackLayout>
|
<StackLayout class="listSpace"> </StackLayout>
|
||||||
</v-template>
|
</v-template>
|
||||||
<v-template>
|
<v-template>
|
||||||
|
@ -30,11 +29,7 @@
|
||||||
</v-template>
|
</v-template>
|
||||||
</ListView>
|
</ListView>
|
||||||
<GridLayout row="1" class="appbar" rows="*" columns="auto, *">
|
<GridLayout row="1" class="appbar" rows="*" columns="auto, *">
|
||||||
<Button
|
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
|
||||||
class="ico"
|
|
||||||
:text="icon.back"
|
|
||||||
@tap="$navigateBack()"
|
|
||||||
/>
|
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</Page>
|
</Page>
|
||||||
|
@ -55,17 +50,17 @@ export default {
|
||||||
items: [
|
items: [
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
icon: "theme",
|
icon: "interface",
|
||||||
title: "intf",
|
title: "intf",
|
||||||
view: Interface,
|
view: Interface,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "cog",
|
icon: "opts",
|
||||||
title: "opts",
|
title: "opts",
|
||||||
view: Options,
|
view: Options,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "exp",
|
icon: "db",
|
||||||
title: "db",
|
title: "db",
|
||||||
view: Database,
|
view: Database,
|
||||||
},
|
},
|
||||||
|
@ -74,11 +69,6 @@ export default {
|
||||||
title: "rest",
|
title: "rest",
|
||||||
view: Reset,
|
view: Reset,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: "help",
|
|
||||||
title: "help",
|
|
||||||
view: Help,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: "info",
|
icon: "info",
|
||||||
title: "About",
|
title: "About",
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
colSpan="2"
|
colSpan="2"
|
||||||
class="options-list"
|
class="options-list"
|
||||||
for="item in items"
|
for="item in items"
|
||||||
@loaded="listViewLoad"
|
|
||||||
>
|
>
|
||||||
<v-template if="$index == 0">
|
<v-template if="$index == 0">
|
||||||
<Label class="pageTitle" :text="'About' | L" />
|
<Label class="pageTitle" :text="'About' | L" />
|
||||||
|
@ -24,7 +23,7 @@
|
||||||
<Label class="info tac tw" :text="'appInfo' | L" />
|
<Label class="info tac tw" :text="'appInfo' | L" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</v-template>
|
</v-template>
|
||||||
<v-template if="$index == 6">
|
<v-template if="$index == 8">
|
||||||
<StackLayout class="listSpace"> </StackLayout>
|
<StackLayout class="listSpace"> </StackLayout>
|
||||||
</v-template>
|
</v-template>
|
||||||
<v-template>
|
<v-template>
|
||||||
|
@ -39,11 +38,7 @@
|
||||||
</v-template>
|
</v-template>
|
||||||
</ListView>
|
</ListView>
|
||||||
<GridLayout row="1" class="appbar" rows="*" columns="auto, *">
|
<GridLayout row="1" class="appbar" rows="*" columns="auto, *">
|
||||||
<Button
|
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
|
||||||
class="ico"
|
|
||||||
:text="icon.back"
|
|
||||||
@tap="$navigateBack()"
|
|
||||||
/>
|
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</Page>
|
</Page>
|
||||||
|
@ -65,6 +60,16 @@ export default {
|
||||||
title: "gh",
|
title: "gh",
|
||||||
url: "https://github.com/vishnuraghavb/EnRecipes",
|
url: "https://github.com/vishnuraghavb/EnRecipes",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: "tg",
|
||||||
|
title: "joinTG",
|
||||||
|
url: "https://t.me/enrecipes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "help",
|
||||||
|
title: "guide",
|
||||||
|
url: "https://github.com/vishnuraghavb/EnRecipes/wiki/User-Guide",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: "priv",
|
icon: "priv",
|
||||||
title: "priv",
|
title: "priv",
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
<template>
|
|
||||||
<Page>
|
|
||||||
<GridLayout class="progressContainer" columns="*, 64">
|
|
||||||
<Progress col="0" :value="backupProgress" maxValue="100" />
|
|
||||||
<Label col="1" :text="` ${backupProgress}%`" />
|
|
||||||
</GridLayout>
|
|
||||||
</Page>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
|
@ -28,7 +28,7 @@
|
||||||
</v-template>
|
</v-template>
|
||||||
</ListView>
|
</ListView>
|
||||||
<GridLayout
|
<GridLayout
|
||||||
v-show="!toast && !backupProgress"
|
v-show="!toast && !progress"
|
||||||
row="1"
|
row="1"
|
||||||
class="appbar"
|
class="appbar"
|
||||||
rows="*"
|
rows="*"
|
||||||
|
@ -49,13 +49,14 @@
|
||||||
</FlexboxLayout>
|
</FlexboxLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
<GridLayout
|
<GridLayout
|
||||||
v-show="backupProgress"
|
v-show="progress"
|
||||||
row="1"
|
row="1"
|
||||||
colSpan="2"
|
colSpan="2"
|
||||||
class="appbar snackBar"
|
class="appbar snackBar"
|
||||||
columns="*"
|
columns="auto, *"
|
||||||
>
|
>
|
||||||
<Progress :value="backupProgress" maxValue="100" />
|
<ActivityIndicator :busy="progress ? true : false" />
|
||||||
|
<Label col="1" class="title" :text="progress" textWrap="true" />
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</Page>
|
</Page>
|
||||||
|
@ -71,9 +72,6 @@ import {
|
||||||
Observable,
|
Observable,
|
||||||
Application,
|
Application,
|
||||||
} from "@nativescript/core";
|
} from "@nativescript/core";
|
||||||
import * as Permissions from "@nativescript-community/perms";
|
|
||||||
import { Zip } from "@nativescript/zip";
|
|
||||||
import { openFilePicker } from "@nativescript-community/ui-document-picker";
|
|
||||||
import { localize } from "@nativescript/localize";
|
import { localize } from "@nativescript/localize";
|
||||||
import ConfirmDialog from "../modal/ConfirmDialog.vue";
|
import ConfirmDialog from "../modal/ConfirmDialog.vue";
|
||||||
import { mapState, mapActions } from "vuex";
|
import { mapState, mapActions } from "vuex";
|
||||||
|
@ -84,7 +82,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
backupFolder: null,
|
backupFolder: null,
|
||||||
backupProgress: 0,
|
progress: null,
|
||||||
toast: null,
|
toast: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -118,7 +116,6 @@ export default {
|
||||||
icon: "imp",
|
icon: "imp",
|
||||||
title: "impBu",
|
title: "impBu",
|
||||||
subTitle: localize("impInfo"),
|
subTitle: localize("impInfo"),
|
||||||
// action: this.importCheck,
|
|
||||||
action: this.openZipFile,
|
action: this.openZipFile,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
|
@ -131,43 +128,63 @@ export default {
|
||||||
"importRecipesAction",
|
"importRecipesAction",
|
||||||
"importMealPlansAction",
|
"importMealPlansAction",
|
||||||
"unlinkBrokenImages",
|
"unlinkBrokenImages",
|
||||||
|
"clearImportSummary",
|
||||||
]),
|
]),
|
||||||
onPageLoad(args) {
|
onPageLoad(args) {
|
||||||
const page = args.object;
|
const page = args.object;
|
||||||
page.bindingContext = new Observable();
|
page.bindingContext = new Observable();
|
||||||
const downloadsFolder = Folder.fromPath(
|
const ContentResolver = Application.android.nativeApp.getContentResolver();
|
||||||
android.os.Environment.getExternalStorageDirectory().getAbsolutePath()
|
this.backupFolder = ApplicationSettings.getString("backupFolder");
|
||||||
).getFolder("Download").path;
|
if (
|
||||||
this.backupFolder = ApplicationSettings.getString(
|
!this.backupFolder ||
|
||||||
"backupFolder",
|
ContentResolver.getPersistedUriPermissions().isEmpty()
|
||||||
downloadsFolder
|
) {
|
||||||
);
|
this.backupFolder = null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// BACKUP FOLDER PICKER
|
// BACKUP FOLDER PICKER
|
||||||
setBackupFolder() {
|
setBackupFolder(startExport) {
|
||||||
utils.pickFolder().then((res) => {
|
const ContentResolver = Application.android.nativeApp.getContentResolver();
|
||||||
if (res != null) {
|
const FLAGS =
|
||||||
this.backupFolder = res;
|
android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION |
|
||||||
|
android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
||||||
|
utils.getBackupFolder().then((uri) => {
|
||||||
|
if (uri != null) {
|
||||||
|
if (this.backupFolder)
|
||||||
|
ContentResolver.releasePersistableUriPermission(
|
||||||
|
new android.net.Uri.parse(this.backupFolder),
|
||||||
|
FLAGS
|
||||||
|
);
|
||||||
|
this.backupFolder = uri.toString();
|
||||||
ApplicationSettings.setString("backupFolder", this.backupFolder);
|
ApplicationSettings.setString("backupFolder", this.backupFolder);
|
||||||
|
// PERSIST PERMISSIONS
|
||||||
|
ContentResolver.takePersistableUriPermission(uri, FLAGS);
|
||||||
|
if (startExport && this.backupFolder) {
|
||||||
|
this.exportBackup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// EXPORT HANDLERS
|
// EXPORT HANDLERS
|
||||||
exportCheck() {
|
exportCheck() {
|
||||||
|
const ContentResolver = Application.android.nativeApp.getContentResolver();
|
||||||
if (!this.recipes.length) {
|
if (!this.recipes.length) {
|
||||||
this.toast = localize("aFBu");
|
this.toast = localize("aFBu");
|
||||||
utils.timer(5, (val) => {
|
utils.timer(5, (val) => {
|
||||||
if (!val) this.toast = val;
|
if (!val) this.toast = val;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.permissionCheck(
|
if (
|
||||||
this.permissionConfirmation,
|
!this.backupFolder ||
|
||||||
localize("reqAcc"),
|
ContentResolver.getPersistedUriPermissions().isEmpty()
|
||||||
this.exportBackup
|
) {
|
||||||
);
|
this.setBackupFolder(true);
|
||||||
|
} else this.exportBackup();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
exportBackup() {
|
exportBackup() {
|
||||||
|
this.progress = localize("expip");
|
||||||
this.exportFiles("create");
|
this.exportFiles("create");
|
||||||
let date = new Date();
|
let date = new Date();
|
||||||
let formattedDate =
|
let formattedDate =
|
||||||
|
@ -183,17 +200,11 @@ export default {
|
||||||
|
|
||||||
let filename = `EnRecipes_${formattedDate}.zip`;
|
let filename = `EnRecipes_${formattedDate}.zip`;
|
||||||
let fromPath = path.join(knownFolders.documents().path, "EnRecipes");
|
let fromPath = path.join(knownFolders.documents().path, "EnRecipes");
|
||||||
let sdcard = android.os.Environment.isExternalStorageManager();
|
utils.Zip.zip(fromPath, this.backupFolder, filename)
|
||||||
let destPath = path.join(this.backupFolder, filename);
|
.then((res) => {
|
||||||
console.log(sdcard);
|
if (res) this.showExportSummary(filename);
|
||||||
Zip.zip({
|
})
|
||||||
directory: fromPath,
|
.catch((err) => console.log("Backup error: ", err));
|
||||||
archive: destPath,
|
|
||||||
onProgress: (progress) => (this.backupProgress = progress),
|
|
||||||
}).then(() => {
|
|
||||||
this.showExportSummary(filename);
|
|
||||||
this.exportFiles("delete");
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
exportFiles(option) {
|
exportFiles(option) {
|
||||||
const folder = path.join(knownFolders.documents().path, "EnRecipes");
|
const folder = path.join(knownFolders.documents().path, "EnRecipes");
|
||||||
|
@ -240,80 +251,140 @@ export default {
|
||||||
this.units.length && userUnitsFile.remove();
|
this.units.length && userUnitsFile.remove();
|
||||||
this.mealPlans.length && mealPlansFile.remove();
|
this.mealPlans.length && mealPlansFile.remove();
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
writeDataToFile(file, data) {
|
writeDataToFile(file, data) {
|
||||||
file.writeText(JSON.stringify(data));
|
file.writeText(JSON.stringify(data));
|
||||||
},
|
},
|
||||||
// IMPORT HANDLERS
|
showExportSummary(filename) {
|
||||||
importCheck() {
|
this.progress = null;
|
||||||
this.permissionCheck(
|
this.$showModal(ConfirmDialog, {
|
||||||
this.permissionConfirmation,
|
props: {
|
||||||
localize("reqAcc"),
|
title: "expSuc",
|
||||||
this.openZipFile
|
description: `Backed up to ${filename}`,
|
||||||
);
|
okButtonText: "OK",
|
||||||
},
|
},
|
||||||
openZipFile() {
|
|
||||||
const ContentResolver = Application.android.nativeApp.getContentResolver();
|
|
||||||
|
|
||||||
utils.getBackupFile().then((uri) => {
|
|
||||||
console.log(uri);
|
|
||||||
const inputStream = ContentResolver.openInputStream(uri);
|
|
||||||
console.log(inputStream);
|
|
||||||
let newFile = knownFolders.temp().getFile("test.zip");
|
|
||||||
console.log(newFile);
|
|
||||||
try {
|
|
||||||
const input = new java.io.BufferedInputStream(inputStream);
|
|
||||||
console.log(input);
|
|
||||||
const outputStream = new java.io.BufferedOutputStream(
|
|
||||||
new java.io.FileOutputStream(newFile.path)
|
|
||||||
);
|
|
||||||
console.log(outputStream);
|
|
||||||
let size = inputStream.available();
|
|
||||||
console.log(size);
|
|
||||||
let buffer = Array.create("byte", size);
|
|
||||||
input.read(buffer);
|
|
||||||
do {
|
|
||||||
outputStream.write(buffer);
|
|
||||||
} while (input.read(buffer) != -1);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
} finally {
|
|
||||||
outputStream.flush();
|
|
||||||
outputStream.close();
|
|
||||||
input.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(zipInputStream);
|
|
||||||
|
|
||||||
// console.log(zipInputStream);
|
|
||||||
// let extractedFile = new java.io.File(
|
|
||||||
// knownFolders.temp().path + "/tmp.zip"
|
|
||||||
// );
|
|
||||||
// if (!extractedFile.exists()) {
|
|
||||||
// extractedFile.mkdirs();
|
|
||||||
// }
|
|
||||||
// console.log(extractedFile);
|
|
||||||
// let outputStream = new java.io.OutputStream(extractedFile);
|
|
||||||
|
|
||||||
// android.os.FileUtils.copy(inputStream, outputStream);
|
|
||||||
// let outputStream = new java.io.FileOutputStream(extractedFile);
|
|
||||||
// console.log(outputStream);
|
|
||||||
// while ((readLen = zipInputStream.read(readBuffer)) != -1) {
|
|
||||||
// outputStream.write(readBuffer, 0, readLen);
|
|
||||||
// }
|
|
||||||
// console.log(outputStream, 2);
|
|
||||||
});
|
});
|
||||||
// openFilePicker({
|
|
||||||
// extensions: ["zip"],
|
|
||||||
// }).then((res) => this.validateZipContent(res.files[0]));
|
|
||||||
},
|
},
|
||||||
importDataToDB(data, db, zipPath) {
|
|
||||||
|
// IMPORT HANDLERS
|
||||||
|
openZipFile() {
|
||||||
|
utils.getBackupFile().then((uri) => {
|
||||||
|
let dest = path.join(knownFolders.temp().path, "tempUnZip");
|
||||||
|
utils.Zip.unzip(uri, dest)
|
||||||
|
.then((res) => {
|
||||||
|
if (res) this.validateZipContent(res, uri);
|
||||||
|
})
|
||||||
|
.catch(() => this.failedImport(localize("buInc")));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
validateZipContent(extractedFolderPath, uri) {
|
||||||
|
this.progress = localize("impip");
|
||||||
|
let cacheFolderPath = extractedFolderPath + "/EnRecipes";
|
||||||
|
const EnRecipesFilePath = cacheFolderPath + "/recipes.json";
|
||||||
|
const ImagesFolderPath = cacheFolderPath + "/Images";
|
||||||
|
const userCuisinesFilePath = cacheFolderPath + "/userCuisines.json";
|
||||||
|
const userCategoriesFilePath = cacheFolderPath + "/userCategories.json";
|
||||||
|
const userYieldUnitsFilePath = cacheFolderPath + "/userYieldUnits.json";
|
||||||
|
const userUnitsFilePath = cacheFolderPath + "/userUnits.json";
|
||||||
|
const mealPlansFilePath = cacheFolderPath + "/mealPlans.json";
|
||||||
|
if (Folder.exists(cacheFolderPath)) {
|
||||||
|
this.isFileDataValid([
|
||||||
|
{
|
||||||
|
path: EnRecipesFilePath,
|
||||||
|
db: "EnRecipesDB",
|
||||||
|
file: "recipes.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: userCuisinesFilePath,
|
||||||
|
db: "userCuisinesDB",
|
||||||
|
file: "userCuisines.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: userCategoriesFilePath,
|
||||||
|
db: "userCategoriesDB",
|
||||||
|
file: "userCategories.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: userYieldUnitsFilePath,
|
||||||
|
db: "userYieldUnitsDB",
|
||||||
|
file: "userYieldUnits.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: userUnitsFilePath,
|
||||||
|
db: "userUnitsDB",
|
||||||
|
file: "userUnits.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: mealPlansFilePath,
|
||||||
|
db: "mealPlansDB",
|
||||||
|
file: "mealPlans.json",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
Folder.fromPath(extractedFolderPath).remove();
|
||||||
|
this.progress = null;
|
||||||
|
this.failedImport(localize("buInc"));
|
||||||
|
}
|
||||||
|
if (Folder.exists(ImagesFolderPath)) {
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
if (this.importSummary.found) {
|
||||||
|
this.importImages(uri, extractedFolderPath);
|
||||||
|
clearInterval(timer);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isFileDataValid(file) {
|
||||||
|
const files = file.filter((e) => File.exists(e.path));
|
||||||
|
if (files.length) {
|
||||||
|
let isValid = files.map((e) => false);
|
||||||
|
files.forEach((file, i) => {
|
||||||
|
File.fromPath(file.path)
|
||||||
|
.readText()
|
||||||
|
.then((data) => {
|
||||||
|
isValid[i] = this.hasValidJSON(data);
|
||||||
|
if (!isValid[i]) {
|
||||||
|
this.failedImport(
|
||||||
|
`${localize("buMod")}\n\n${localize("invFile")}: ${file.file}`
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (isValid.every((e) => e === true)) {
|
||||||
|
files.forEach((file) => {
|
||||||
|
File.fromPath(file.path)
|
||||||
|
.readText()
|
||||||
|
.then((data) => {
|
||||||
|
this.importDataToDB(JSON.parse(data), file.db);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.failedImport(localize("buEmp"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
failedImport(description) {
|
||||||
|
this.$showModal(ConfirmDialog, {
|
||||||
|
props: {
|
||||||
|
title: "impFail",
|
||||||
|
description,
|
||||||
|
okButtonText: "OK",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
hasValidJSON(data) {
|
||||||
|
try {
|
||||||
|
JSON.parse(data) && Array.isArray(JSON.parse(data));
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
importDataToDB(data, db) {
|
||||||
switch (db) {
|
switch (db) {
|
||||||
case "EnRecipesDB":
|
case "EnRecipesDB":
|
||||||
this.importImages(zipPath);
|
|
||||||
this.importRecipesAction(data);
|
this.importRecipesAction(data);
|
||||||
break;
|
break;
|
||||||
case "userCuisinesDB":
|
case "userCuisinesDB":
|
||||||
|
@ -343,133 +414,18 @@ export default {
|
||||||
case "mealPlansDB":
|
case "mealPlansDB":
|
||||||
this.importMealPlansAction(data);
|
this.importMealPlansAction(data);
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hasValidJSON(data) {
|
importImages(uri, extractedFolderPath) {
|
||||||
try {
|
let destPath = knownFolders.documents().path;
|
||||||
JSON.parse(data) && Array.isArray(JSON.parse(data));
|
Folder.fromPath(destPath);
|
||||||
} catch (e) {
|
utils.Zip.unzip(uri, destPath).then((res) => {
|
||||||
return false;
|
if (res) {
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
isFileDataValid(file) {
|
|
||||||
const files = file.filter((e) => File.exists(e.path));
|
|
||||||
if (files.length) {
|
|
||||||
let isValid = files.map((e) => false);
|
|
||||||
files.forEach((file, i) => {
|
|
||||||
File.fromPath(file.path)
|
|
||||||
.readText()
|
|
||||||
.then((data) => {
|
|
||||||
isValid[i] = this.hasValidJSON(data);
|
|
||||||
if (!isValid[i]) {
|
|
||||||
this.failedImport(
|
|
||||||
`${localize("buMod")}\n\n${localize("invFile")}: ${file.file}`
|
|
||||||
);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (isValid.every((e) => e === true)) {
|
|
||||||
files.forEach((file, i) => {
|
|
||||||
File.fromPath(file.path)
|
|
||||||
.readText()
|
|
||||||
.then((data) => {
|
|
||||||
this.importDataToDB(
|
|
||||||
JSON.parse(data),
|
|
||||||
file.db,
|
|
||||||
file.zipPath
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.failedImport(localize("buEmp"));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
failedImport(description) {
|
|
||||||
this.$showModal(ConfirmDialog, {
|
|
||||||
props: {
|
|
||||||
title: "impFail",
|
|
||||||
description,
|
|
||||||
okButtonText: "OK",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
validateZipContent(zipPath) {
|
|
||||||
console.log(zipPath);
|
|
||||||
Zip.unzip({
|
|
||||||
archive: zipPath,
|
|
||||||
overwrite: true,
|
|
||||||
onProgress: (progress) => (this.backupProgress = progress),
|
|
||||||
}).then((extractedFolderPath) => {
|
|
||||||
let cacheFolderPath = extractedFolderPath + "/EnRecipes";
|
|
||||||
const EnRecipesFilePath = cacheFolderPath + "/recipes.json";
|
|
||||||
const userCuisinesFilePath = cacheFolderPath + "/userCuisines.json";
|
|
||||||
const userCategoriesFilePath = cacheFolderPath + "/userCategories.json";
|
|
||||||
const userYieldUnitsFilePath = cacheFolderPath + "/userYieldUnits.json";
|
|
||||||
const userUnitsFilePath = cacheFolderPath + "/userUnits.json";
|
|
||||||
const mealPlansFilePath = cacheFolderPath + "/mealPlans.json";
|
|
||||||
if (Folder.exists(cacheFolderPath)) {
|
|
||||||
this.isFileDataValid([
|
|
||||||
{
|
|
||||||
zipPath,
|
|
||||||
path: EnRecipesFilePath,
|
|
||||||
db: "EnRecipesDB",
|
|
||||||
file: "recipes.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
zipPath,
|
|
||||||
path: userCuisinesFilePath,
|
|
||||||
db: "userCuisinesDB",
|
|
||||||
file: "userCuisines.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
zipPath,
|
|
||||||
path: userCategoriesFilePath,
|
|
||||||
db: "userCategoriesDB",
|
|
||||||
file: "userCategories.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
zipPath,
|
|
||||||
path: userYieldUnitsFilePath,
|
|
||||||
db: "userYieldUnitsDB",
|
|
||||||
file: "userYieldUnits.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
zipPath,
|
|
||||||
path: userUnitsFilePath,
|
|
||||||
db: "userUnitsDB",
|
|
||||||
file: "userUnits.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
zipPath,
|
|
||||||
path: mealPlansFilePath,
|
|
||||||
db: "mealPlansDB",
|
|
||||||
file: "mealPlans.json",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
Folder.fromPath(extractedFolderPath).remove();
|
|
||||||
this.failedImport(localize("buInc"));
|
|
||||||
}
|
|
||||||
if (Folder.exists(cacheFolderPath + "/Images")) {
|
|
||||||
this.importImages(cacheFolderPath + "/Images");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
importImages(sourcePath) {
|
|
||||||
let dest = knownFolders.documents().path;
|
|
||||||
Zip.unzip({
|
|
||||||
archive: sourcePath,
|
|
||||||
directory: dest,
|
|
||||||
overwrite: true,
|
|
||||||
onProgress: (progress) => (this.backupProgress = progress),
|
|
||||||
}).then((res) => {
|
|
||||||
this.showImportSummary();
|
this.showImportSummary();
|
||||||
this.unlinkBrokenImages();
|
this.unlinkBrokenImages();
|
||||||
|
this.exportFiles("delete");
|
||||||
|
Folder.fromPath(extractedFolderPath).remove();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
showImportSummary() {
|
showImportSummary() {
|
||||||
|
@ -478,6 +434,7 @@ export default {
|
||||||
let importedNote = `\n${imported} ${localize("recI")}`;
|
let importedNote = `\n${imported} ${localize("recI")}`;
|
||||||
let existsNote = `\n${exists} ${localize("recE")}`;
|
let existsNote = `\n${exists} ${localize("recE")}`;
|
||||||
let updatedNote = `\n${updated} ${localize("recU")}`;
|
let updatedNote = `\n${updated} ${localize("recU")}`;
|
||||||
|
this.progress = null;
|
||||||
this.$showModal(ConfirmDialog, {
|
this.$showModal(ConfirmDialog, {
|
||||||
props: {
|
props: {
|
||||||
title: "impSuc",
|
title: "impSuc",
|
||||||
|
@ -486,51 +443,9 @@ export default {
|
||||||
)}${importedNote}${existsNote}${updatedNote}`,
|
)}${importedNote}${existsNote}${updatedNote}`,
|
||||||
okButtonText: "OK",
|
okButtonText: "OK",
|
||||||
},
|
},
|
||||||
}).then(() => (this.backupProgress = 0));
|
}).then(() => this.clearImportSummary());
|
||||||
},
|
|
||||||
showExportSummary(filename) {
|
|
||||||
this.$showModal(ConfirmDialog, {
|
|
||||||
props: {
|
|
||||||
title: "expSuc",
|
|
||||||
description: `Backed up to ${filename}`,
|
|
||||||
okButtonText: "OK",
|
|
||||||
},
|
|
||||||
}).then(() => (this.backupProgress = 0));
|
|
||||||
},
|
|
||||||
// PERMISSIONS HANDLER
|
|
||||||
permissionCheck(confirmation, description, action) {
|
|
||||||
if (!ApplicationSettings.getBoolean("storagePermissionAsked", false)) {
|
|
||||||
confirmation(description).then((e) => {
|
|
||||||
if (e) {
|
|
||||||
Permissions.request("photo").then((res) => {
|
|
||||||
let status = res[Object.keys(res)[0]];
|
|
||||||
if (status === "authorized") action();
|
|
||||||
if (status !== "denied")
|
|
||||||
ApplicationSettings.setBoolean("storagePermissionAsked", true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Permissions.check("photo").then((res) => {
|
|
||||||
let status = res[Object.keys(res)[0]];
|
|
||||||
if (status !== "authorized") {
|
|
||||||
confirmation(description).then((e) => {
|
|
||||||
e && utils.openAppSettingsPage();
|
|
||||||
});
|
|
||||||
} else action();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
permissionConfirmation(description) {
|
|
||||||
return this.$showModal(ConfirmDialog, {
|
|
||||||
props: {
|
|
||||||
title: "grant",
|
|
||||||
description,
|
|
||||||
cancelButtonText: "nNBtn",
|
|
||||||
okButtonText: "conBtn",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
touch({ object, action }, method) {
|
touch({ object, action }, method) {
|
||||||
object.className = action.match(/down|move/) ? "option fade" : "option";
|
object.className = action.match(/down|move/) ? "option fade" : "option";
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
rowSpan="2"
|
rowSpan="2"
|
||||||
class="options-list"
|
class="options-list"
|
||||||
for="item in items"
|
for="item in items"
|
||||||
@loaded="listViewLoad"
|
|
||||||
>
|
>
|
||||||
<v-template if="$index == 0">
|
<v-template if="$index == 0">
|
||||||
<Label class="pageTitle" :text="'help' | L" />
|
<Label class="pageTitle" :text="'help' | L" />
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
colSpan="2"
|
colSpan="2"
|
||||||
rowSpan="2"
|
rowSpan="2"
|
||||||
class="options-list"
|
class="options-list"
|
||||||
@loaded="listViewLoad"
|
|
||||||
for="item in items"
|
for="item in items"
|
||||||
>
|
>
|
||||||
<v-template if="$index == 0">
|
<v-template if="$index == 0">
|
||||||
|
@ -29,11 +28,7 @@
|
||||||
</v-template>
|
</v-template>
|
||||||
</ListView>
|
</ListView>
|
||||||
<GridLayout row="1" class="appbar" rows="*" columns="auto, *">
|
<GridLayout row="1" class="appbar" rows="*" columns="auto, *">
|
||||||
<Button
|
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
|
||||||
class="ico"
|
|
||||||
:text="icon.back"
|
|
||||||
@tap="$navigateBack()"
|
|
||||||
/>
|
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</Page>
|
</Page>
|
||||||
|
@ -50,6 +45,7 @@ import { localize, overrideLocale } from "@nativescript/localize";
|
||||||
import ActionDialog from "../modal/ActionDialog.vue";
|
import ActionDialog from "../modal/ActionDialog.vue";
|
||||||
import ConfirmDialog from "../modal/ConfirmDialog.vue";
|
import ConfirmDialog from "../modal/ConfirmDialog.vue";
|
||||||
import { mapState, mapActions } from "vuex";
|
import { mapState, mapActions } from "vuex";
|
||||||
|
import * as utils from "~/shared/utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
@ -75,7 +71,7 @@ export default {
|
||||||
action: this.selectThemes,
|
action: this.selectThemes,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "l1",
|
icon: "layout",
|
||||||
title: "listVM",
|
title: "listVM",
|
||||||
subTitle: localize(this.layout),
|
subTitle: localize(this.layout),
|
||||||
action: this.setLayoutMode,
|
action: this.setLayoutMode,
|
||||||
|
@ -142,7 +138,7 @@ export default {
|
||||||
this.$showModal(ActionDialog, {
|
this.$showModal(ActionDialog, {
|
||||||
props: {
|
props: {
|
||||||
title: "List view mode",
|
title: "List view mode",
|
||||||
list: ["Detailed", "Grid", "Simple", "Minimal"],
|
list: ["detailed", "grid", "photogrid", "simple", "minimal"],
|
||||||
},
|
},
|
||||||
}).then((action) => {
|
}).then((action) => {
|
||||||
if (action && action !== "Cancel" && this.layoutMode !== action) {
|
if (action && action !== "Cancel" && this.layoutMode !== action) {
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
colSpan="2"
|
colSpan="2"
|
||||||
rowSpan="2"
|
rowSpan="2"
|
||||||
class="options-list"
|
class="options-list"
|
||||||
@loaded="listViewLoad"
|
|
||||||
for="item in items"
|
for="item in items"
|
||||||
>
|
>
|
||||||
<v-template if="$index == 0">
|
<v-template if="$index == 0">
|
||||||
|
@ -14,15 +13,18 @@
|
||||||
<v-template if="item.type == 'switch'">
|
<v-template if="item.type == 'switch'">
|
||||||
<GridLayout columns="auto, *, auto" class="option">
|
<GridLayout columns="auto, *, auto" class="option">
|
||||||
<Label class="ico" :text="icon[item.icon]" />
|
<Label class="ico" :text="icon[item.icon]" />
|
||||||
<StackLayout col="1">
|
<StackLayout col="1" verticalAlignment="center">
|
||||||
<Label :text="item.title | L" class="info" />
|
<Label :text="item.title | L" class="info" />
|
||||||
<Label :text="item.subTitle | L" class="sub" />
|
<Label
|
||||||
|
v-if="item.subTitle"
|
||||||
|
:text="item.subTitle | L"
|
||||||
|
class="sub"
|
||||||
|
/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Switch
|
<Switch
|
||||||
:color="item.subAction ? '#ff5200' : '#858585'"
|
:color="item.checked ? '#ff5200' : '#adb5bd'"
|
||||||
verticalAlignment="center"
|
|
||||||
col="2"
|
col="2"
|
||||||
:checked="item.subAction"
|
:checked="item.checked"
|
||||||
@checkedChange="item.action"
|
@checkedChange="item.action"
|
||||||
/>
|
/>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
|
@ -31,12 +33,26 @@
|
||||||
<StackLayout class="listSpace"> </StackLayout>
|
<StackLayout class="listSpace"> </StackLayout>
|
||||||
</v-template>
|
</v-template>
|
||||||
</ListView>
|
</ListView>
|
||||||
<GridLayout row="1" class="appbar" rows="*" columns="auto, *">
|
<GridLayout
|
||||||
<Button
|
v-show="!toast"
|
||||||
class="ico"
|
row="1"
|
||||||
:text="icon.back"
|
class="appbar"
|
||||||
@tap="$navigateBack()"
|
rows="*"
|
||||||
/>
|
columns="auto, *"
|
||||||
|
>
|
||||||
|
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
|
||||||
|
</GridLayout>
|
||||||
|
<GridLayout
|
||||||
|
v-show="toast"
|
||||||
|
row="1"
|
||||||
|
colSpan="2"
|
||||||
|
class="appbar snackBar"
|
||||||
|
columns="*"
|
||||||
|
@tap="toast = null"
|
||||||
|
>
|
||||||
|
<FlexboxLayout minHeight="48" alignItems="center">
|
||||||
|
<Label class="title msg" :text="toast" />
|
||||||
|
</FlexboxLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</Page>
|
</Page>
|
||||||
|
@ -44,13 +60,17 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ApplicationSettings, Observable } from "@nativescript/core";
|
import { ApplicationSettings, Observable } from "@nativescript/core";
|
||||||
import { localize } from "@nativescript/localize";
|
|
||||||
import { mapState, mapActions } from "vuex";
|
import { mapState, mapActions } from "vuex";
|
||||||
import * as utils from "~/shared/utils";
|
import * as utils from "~/shared/utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
toast: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["icon", "shakeEnabled"]),
|
...mapState(["icon", "shakeEnabled", "mondayFirst"]),
|
||||||
items() {
|
items() {
|
||||||
return [
|
return [
|
||||||
{},
|
{},
|
||||||
|
@ -59,29 +79,39 @@ export default {
|
||||||
icon: "shuf",
|
icon: "shuf",
|
||||||
title: "sVw",
|
title: "sVw",
|
||||||
subTitle: "sVwInfo",
|
subTitle: "sVwInfo",
|
||||||
|
checked: this.shakeEnabled,
|
||||||
action: this.toggleShake,
|
action: this.toggleShake,
|
||||||
subAction: this.shakeEnabled,
|
},
|
||||||
|
{
|
||||||
|
type: "switch",
|
||||||
|
icon: "week",
|
||||||
|
title: "swm",
|
||||||
|
checked: this.mondayFirst,
|
||||||
|
action: this.toggleFirstDay,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(["setShakeAction"]),
|
...mapActions(["setShake", "setFirstDay"]),
|
||||||
onPageLoad(args) {
|
onPageLoad(args) {
|
||||||
const page = args.object;
|
const page = args.object;
|
||||||
page.bindingContext = new Observable();
|
page.bindingContext = new Observable();
|
||||||
},
|
},
|
||||||
|
toggleFirstDay({ object }) {
|
||||||
|
this.setFirstDay(object.checked);
|
||||||
|
},
|
||||||
// SHAKE VIEW RANDOM RECIPE
|
// SHAKE VIEW RANDOM RECIPE
|
||||||
toggleShake(args) {
|
toggleShake({ object }) {
|
||||||
let checked = args.object.checked;
|
let checked = object.checked;
|
||||||
if (checked && !utils.hasAccelerometer()) {
|
if (checked && !utils.hasAccelerometer()) {
|
||||||
args.object.checked = false;
|
checked = false;
|
||||||
// Toast.makeText(localize("noAccSensor"), "long").show();
|
this.toast = localize("noAccSensor");
|
||||||
} else {
|
utils.timer(5, (val) => {
|
||||||
ApplicationSettings.setBoolean("shakeEnabled", checked);
|
if (!val) this.toast = val;
|
||||||
this.setShakeAction(checked);
|
});
|
||||||
}
|
} else this.setShake(checked);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
colSpan="2"
|
colSpan="2"
|
||||||
rowSpan="2"
|
rowSpan="2"
|
||||||
class="options-list"
|
class="options-list"
|
||||||
@loaded="listViewLoad"
|
|
||||||
for="item in items"
|
for="item in items"
|
||||||
>
|
>
|
||||||
<v-template if="$index == 0">
|
<v-template if="$index == 0">
|
||||||
|
@ -28,9 +27,27 @@
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</v-template>
|
</v-template>
|
||||||
</ListView>
|
</ListView>
|
||||||
<GridLayout row="1" class="appbar" rows="*" columns="auto, *">
|
<GridLayout
|
||||||
|
v-show="!toast"
|
||||||
|
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
|
||||||
|
v-show="toast"
|
||||||
|
row="1"
|
||||||
|
colSpan="2"
|
||||||
|
class="appbar snackBar"
|
||||||
|
columns="*"
|
||||||
|
@tap="toast = null"
|
||||||
|
>
|
||||||
|
<FlexboxLayout minHeight="48" alignItems="center">
|
||||||
|
<Label class="title msg" :text="toast" />
|
||||||
|
</FlexboxLayout>
|
||||||
|
</GridLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
@ -39,8 +56,13 @@
|
||||||
import { Observable } from "@nativescript/core";
|
import { Observable } from "@nativescript/core";
|
||||||
import { localize } from "@nativescript/localize";
|
import { localize } from "@nativescript/localize";
|
||||||
import { mapState, mapActions } from "vuex";
|
import { mapState, mapActions } from "vuex";
|
||||||
|
import * as utils from "~/shared/utils";
|
||||||
export default {
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
toast: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["icon"]),
|
...mapState(["icon"]),
|
||||||
items() {
|
items() {
|
||||||
|
@ -76,7 +98,10 @@ export default {
|
||||||
// RESET
|
// RESET
|
||||||
resetListItems(listName) {
|
resetListItems(listName) {
|
||||||
this.resetListItemsAction(listName);
|
this.resetListItemsAction(listName);
|
||||||
// Toast.makeText(localize("restDone")).show();
|
this.toast = localize("restDone");
|
||||||
|
utils.timer(5, (val) => {
|
||||||
|
if (!val) this.toast = val;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
touch({ object, action }, type) {
|
touch({ object, action }, type) {
|
||||||
object.className = action.match(/down|move/) ? "option fade" : "option";
|
object.className = action.match(/down|move/) ? "option fade" : "option";
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
dock="top"
|
dock="top"
|
||||||
rows="auto,auto"
|
rows="auto,auto"
|
||||||
columns="*, auto"
|
columns="*, auto"
|
||||||
paddingBottom="8"
|
paddingBottom="24"
|
||||||
:class="{ topPlate: scrolled }"
|
|
||||||
>
|
>
|
||||||
<StackLayout>
|
<StackLayout>
|
||||||
<Label class="pageTitle" paddingBottom="8" :text="recipe.title" />
|
<Label class="pageTitle" paddingBottom="8" :text="recipe.title" />
|
||||||
|
@ -33,81 +32,67 @@
|
||||||
class="photo"
|
class="photo"
|
||||||
decodeWidth="96"
|
decodeWidth="96"
|
||||||
decodeHeight="96"
|
decodeHeight="96"
|
||||||
|
@tap="viewPhoto"
|
||||||
/>
|
/>
|
||||||
<StackLayout row="1" colSpan="2" orientation="horizontal" margin="16">
|
|
||||||
<GridLayout
|
|
||||||
rows="48"
|
|
||||||
columns="auto, auto"
|
|
||||||
class="segment"
|
|
||||||
v-for="(item, index) in topmenu"
|
|
||||||
:key="index"
|
|
||||||
:class="{ select: selectedIndex == index }"
|
|
||||||
@touch="touchSelector($event, index, item.icon)"
|
|
||||||
>
|
|
||||||
<Label class="ico" :text="icon[item.icon]" />
|
|
||||||
<Label class="value" :text="item.count" col="1" />
|
|
||||||
</GridLayout>
|
|
||||||
</StackLayout>
|
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
dock="bottom"
|
dock="bottom"
|
||||||
@loaded="onScrollLoad"
|
@loaded="onScrollLoad"
|
||||||
@scroll="!toast && onScroll($event)"
|
@scroll="!toast && onScroll($event)"
|
||||||
>
|
>
|
||||||
<StackLayout>
|
|
||||||
<StackLayout>
|
<StackLayout>
|
||||||
<GridLayout rows="auto" columns="*, *">
|
<GridLayout rows="auto" columns="*, *">
|
||||||
<StackLayout class="attribute">
|
<StackLayout class="attribute">
|
||||||
<Label class="title" :text="'cui' | L" />
|
<Label class="title sub" :text="'cui' | L" />
|
||||||
<Label class="value" :text="recipe.cuisine | L" />
|
<Label class="value" :text="recipe.cuisine | L" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout class="attribute" col="1">
|
<StackLayout class="attribute" col="1">
|
||||||
<Label class="title" :text="'cat' | L" />
|
<Label class="title sub" :text="'cat' | L" />
|
||||||
<Label class="value" :text="recipe.category | L" />
|
<Label class="value" :text="recipe.category | L" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
<StackLayout v-if="recipe.tags.length" class="attribute">
|
<StackLayout :hidden="!recipe.tags.length" class="attribute">
|
||||||
<Label class="title" :text="'ts' | L" />
|
<Label class="title sub" :text="'ts' | L" />
|
||||||
<Label class="value" :text="getTags(recipe.tags)" />
|
<Label class="value" :text="getTags(recipe.tags)" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<GridLayout rows="auto" columns="*, *">
|
<GridLayout rows="auto" columns="*, *">
|
||||||
<StackLayout class="attribute" v-if="hasTime(recipe.prepTime)">
|
<StackLayout
|
||||||
<Label class="title" :text="'prepT' | L" />
|
class="attribute"
|
||||||
|
:hidden="!hasTime(recipe.prepTime)"
|
||||||
|
>
|
||||||
|
<Label class="title sub" :text="'prepT' | L" />
|
||||||
<Label class="value" :text="formattedTime(recipe.prepTime)" />
|
<Label class="value" :text="formattedTime(recipe.prepTime)" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
:col="hasTime(recipe.prepTime) ? 1 : 0"
|
:col="hasTime(recipe.prepTime) ? 1 : 0"
|
||||||
class="attribute"
|
class="attribute"
|
||||||
v-if="hasTime(recipe.cookTime)"
|
:hidden="!hasTime(recipe.cookTime)"
|
||||||
>
|
>
|
||||||
<Label class="title" :text="'cookT' | L" />
|
<Label class="title sub" :text="'cookT' | L" />
|
||||||
<Label class="value" :text="formattedTime(recipe.cookTime)" />
|
<Label class="value" :text="formattedTime(recipe.cookTime)" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
<GridLayout rows="auto" columns="*, *">
|
<GridLayout rows="auto" columns="*, *">
|
||||||
<StackLayout class="attribute">
|
<StackLayout class="attribute">
|
||||||
<Label class="title" :text="'yld' | L" />
|
<Label class="title sub" :text="'yld' | L" />
|
||||||
<Label
|
<Label
|
||||||
@touch="touchYield"
|
@touch="touchYield"
|
||||||
class="value clickable"
|
class="value clickable"
|
||||||
:text="`${positiveYieldMultiplier} ${$options.filters.L(
|
:text="`${tempYieldQuantity} ${$options.filters.L(
|
||||||
recipe.yield.unit
|
recipe.yield.unit
|
||||||
)}`"
|
)}`"
|
||||||
/>
|
/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout class="attribute" col="1">
|
<StackLayout class="attribute" col="1">
|
||||||
<Label class="title" :text="'Difficulty level' | L" />
|
<Label class="title sub" :text="'Difficulty level' | L" />
|
||||||
<Label class="value" :text="recipe.difficulty | L" />
|
<Label class="value" :text="recipe.difficulty | L" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</StackLayout>
|
|
||||||
<StackLayout>
|
|
||||||
<Label
|
<Label
|
||||||
id="items"
|
|
||||||
padding="0 16"
|
padding="0 16"
|
||||||
class="sectionTitle"
|
class="sectionTitle"
|
||||||
v-show="recipe.ingredients.length"
|
:hidden="!recipe.ingredients.length"
|
||||||
:text="'ings' | L"
|
:text="getTitleCount('ings', 'ingredients')"
|
||||||
/>
|
/>
|
||||||
<StackLayout @loaded="onIngsLoad">
|
<StackLayout @loaded="onIngsLoad">
|
||||||
<GridLayout
|
<GridLayout
|
||||||
|
@ -118,7 +103,7 @@
|
||||||
class="ingredient"
|
class="ingredient"
|
||||||
@touch="touchIngredient($event, index)"
|
@touch="touchIngredient($event, index)"
|
||||||
>
|
>
|
||||||
<Button class="ico min" :text="icon.done" />
|
<Button class="ico min" :text="icon.uncheck" />
|
||||||
<Label
|
<Label
|
||||||
class="value tw"
|
class="value tw"
|
||||||
col="1"
|
col="1"
|
||||||
|
@ -135,11 +120,10 @@
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Label
|
<Label
|
||||||
id="steps"
|
|
||||||
padding="0 16"
|
padding="0 16"
|
||||||
v-show="recipe.instructions.length"
|
:hidden="!recipe.instructions.length"
|
||||||
class="sectionTitle"
|
class="sectionTitle"
|
||||||
:text="'ins' | L"
|
:text="getTitleCount('inss', 'instructions')"
|
||||||
/>
|
/>
|
||||||
<StackLayout @loaded="onInsLoad">
|
<StackLayout @loaded="onInsLoad">
|
||||||
<GridLayout
|
<GridLayout
|
||||||
|
@ -154,11 +138,10 @@
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Label
|
<Label
|
||||||
id="comb"
|
|
||||||
padding="0 16"
|
padding="0 16"
|
||||||
v-show="recipe.combinations.length"
|
:hidden="!recipe.combinations.length"
|
||||||
class="sectionTitle"
|
class="sectionTitle"
|
||||||
:text="'cmbs' | L"
|
:text="getTitleCount('cmbs', 'combinations')"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
v-for="(combination, index) in recipe.combinations"
|
v-for="(combination, index) in recipe.combinations"
|
||||||
|
@ -168,18 +151,23 @@
|
||||||
@tap="viewCombination(combination)"
|
@tap="viewCombination(combination)"
|
||||||
/>
|
/>
|
||||||
<Label
|
<Label
|
||||||
id="notes"
|
|
||||||
padding="0 16"
|
padding="0 16"
|
||||||
v-show="recipe.notes.length"
|
:hidden="!recipe.notes.length"
|
||||||
class="sectionTitle"
|
class="sectionTitle"
|
||||||
:text="'nos' | L"
|
: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
|
||||||
|
)}`"
|
||||||
/>
|
/>
|
||||||
<StackLayout @loaded="onNosLoad" padding="0 16 72"> </StackLayout>
|
|
||||||
</StackLayout>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</DockLayout>
|
</DockLayout>
|
||||||
|
|
||||||
<GridLayout
|
<GridLayout
|
||||||
row="1"
|
row="1"
|
||||||
@loaded="onAppBarLoad"
|
@loaded="onAppBarLoad"
|
||||||
|
@ -187,13 +175,17 @@
|
||||||
v-show="!toast"
|
v-show="!toast"
|
||||||
columns="auto, *, auto, auto, auto, auto"
|
columns="auto, *, auto, auto, auto, auto"
|
||||||
>
|
>
|
||||||
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
|
<Button
|
||||||
|
class="ico"
|
||||||
|
:text="photoOpen ? icon.x : icon.back"
|
||||||
|
@tap="navigateBack"
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
col="2"
|
col="2"
|
||||||
v-if="!filterTrylater"
|
v-if="!filterTrylater"
|
||||||
class="ico"
|
class="ico"
|
||||||
:text="recipe.tried ? icon.try : icon.tried"
|
:text="recipe.tried ? icon.try : icon.tried"
|
||||||
@tap="toggleTrylater"
|
@tap="toggle('tried')"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
col="2"
|
col="2"
|
||||||
|
@ -206,7 +198,7 @@
|
||||||
col="3"
|
col="3"
|
||||||
class="ico"
|
class="ico"
|
||||||
:text="recipe.isFavorite ? icon.faved : icon.fav"
|
:text="recipe.isFavorite ? icon.faved : icon.fav"
|
||||||
@tap="toggleFavourite"
|
@tap="toggle('isFavorite')"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
col="4"
|
col="4"
|
||||||
|
@ -226,8 +218,7 @@
|
||||||
<GridLayout
|
<GridLayout
|
||||||
v-show="toast"
|
v-show="toast"
|
||||||
row="1"
|
row="1"
|
||||||
class="appbar"
|
class="appbar snackBar"
|
||||||
:class="hlMsg && 'hlMsg'"
|
|
||||||
columns="*"
|
columns="*"
|
||||||
@tap="toast = null"
|
@tap="toast = null"
|
||||||
>
|
>
|
||||||
|
@ -235,6 +226,13 @@
|
||||||
<Label class="title msg" :text="toast" />
|
<Label class="title msg" :text="toast" />
|
||||||
</FlexboxLayout>
|
</FlexboxLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
|
<AbsoluteLayout rowSpan="2">
|
||||||
|
<ImageZoom
|
||||||
|
@loaded="onImgZoomLoad"
|
||||||
|
:src="recipe.imageSrc"
|
||||||
|
class="photoviewer"
|
||||||
|
></ImageZoom>
|
||||||
|
</AbsoluteLayout>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
@ -242,6 +240,7 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import {
|
||||||
Application,
|
Application,
|
||||||
|
AndroidApplication,
|
||||||
ImageSource,
|
ImageSource,
|
||||||
Utils,
|
Utils,
|
||||||
Span,
|
Span,
|
||||||
|
@ -249,6 +248,7 @@ import {
|
||||||
Label,
|
Label,
|
||||||
Observable,
|
Observable,
|
||||||
Screen,
|
Screen,
|
||||||
|
CoreTypes,
|
||||||
} from "@nativescript/core";
|
} from "@nativescript/core";
|
||||||
import * as SocialShare from "@nativescript/social-share";
|
import * as SocialShare from "@nativescript/social-share";
|
||||||
import { localize } from "@nativescript/localize";
|
import { localize } from "@nativescript/localize";
|
||||||
|
@ -257,7 +257,7 @@ import { mapActions, mapState } from "vuex";
|
||||||
import EditRecipe from "./EditRecipe.vue";
|
import EditRecipe from "./EditRecipe.vue";
|
||||||
import ActionDialog from "./modal/ActionDialog.vue";
|
import ActionDialog from "./modal/ActionDialog.vue";
|
||||||
import PromptDialog from "./modal/PromptDialog.vue";
|
import PromptDialog from "./modal/PromptDialog.vue";
|
||||||
let toastTimer, scrollTimer;
|
import * as utils from "~/shared/utils";
|
||||||
export default {
|
export default {
|
||||||
props: ["filterTrylater", "recipeID"],
|
props: ["filterTrylater", "recipeID"],
|
||||||
data() {
|
data() {
|
||||||
|
@ -271,42 +271,21 @@ export default {
|
||||||
ingcon: null,
|
ingcon: null,
|
||||||
inscon: null,
|
inscon: null,
|
||||||
notescon: null,
|
notescon: null,
|
||||||
|
imgZoom: null,
|
||||||
checks: [],
|
checks: [],
|
||||||
steps: [],
|
checked: 0,
|
||||||
|
stepsDid: 0,
|
||||||
toast: null,
|
toast: null,
|
||||||
hlMsg: false,
|
photoOpen: false,
|
||||||
selectedIndex: 0,
|
|
||||||
scrollingToId: false,
|
|
||||||
scrolled: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["icon", "recipes"]),
|
...mapState(["icon", "recipes"]),
|
||||||
positiveYieldMultiplier() {
|
tempYieldQuantity() {
|
||||||
return Math.abs(this.yieldMultiplier) > 0
|
return Math.abs(this.yieldMultiplier) > 0
|
||||||
? Math.abs(parseFloat(this.yieldMultiplier))
|
? Math.abs(parseFloat(this.yieldMultiplier))
|
||||||
: 1;
|
: 1;
|
||||||
},
|
},
|
||||||
topmenu() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
icon: "items",
|
|
||||||
count: this.recipe.ingredients.length,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "steps",
|
|
||||||
count: this.recipe.instructions.length,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "comb",
|
|
||||||
count: this.recipe.combinations.length,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "notes",
|
|
||||||
count: this.recipe.notes.length,
|
|
||||||
},
|
|
||||||
].filter((e) => e.count);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions([
|
...mapActions([
|
||||||
|
@ -321,9 +300,7 @@ export default {
|
||||||
const page = args.object;
|
const page = args.object;
|
||||||
page.bindingContext = new Observable();
|
page.bindingContext = new Observable();
|
||||||
this.busy = false;
|
this.busy = false;
|
||||||
setTimeout((e) => {
|
|
||||||
this.setComponent("ViewRecipe");
|
this.setComponent("ViewRecipe");
|
||||||
}, 500);
|
|
||||||
if (this.yieldMultiplier == this.recipe.yield.quantity)
|
if (this.yieldMultiplier == this.recipe.yield.quantity)
|
||||||
this.yieldMultiplier = this.recipe.yield.quantity;
|
this.yieldMultiplier = this.recipe.yield.quantity;
|
||||||
this.keepScreenOn(true);
|
this.keepScreenOn(true);
|
||||||
|
@ -345,27 +322,16 @@ export default {
|
||||||
this.notescon = object;
|
this.notescon = object;
|
||||||
this.createNotes();
|
this.createNotes();
|
||||||
},
|
},
|
||||||
|
|
||||||
onScrollLoad(args) {
|
onScrollLoad(args) {
|
||||||
this.scrollview = args.object;
|
this.scrollview = args.object;
|
||||||
},
|
},
|
||||||
|
onImgZoomLoad({ object }) {
|
||||||
|
this.imgZoom = object;
|
||||||
|
this.imgZoom.visibility = "collapsed";
|
||||||
|
this.imgZoom.top = 24;
|
||||||
|
this.imgZoom.left = Screen.mainScreen.widthDIPs - 112;
|
||||||
|
},
|
||||||
onScroll(args) {
|
onScroll(args) {
|
||||||
if (!this.scrollingToId) {
|
|
||||||
let vm = this;
|
|
||||||
let h = Screen.mainScreen.heightDIPs;
|
|
||||||
function getPos(id) {
|
|
||||||
return vm.scrollview.getViewById(id).getLocationOnScreen().y + 144;
|
|
||||||
}
|
|
||||||
let items = getPos("items") <= h;
|
|
||||||
let steps = getPos("steps") <= h;
|
|
||||||
let comb = getPos("comb") <= h;
|
|
||||||
let notes = getPos("notes") <= h;
|
|
||||||
if (items) this.selectedIndex = 0;
|
|
||||||
if (steps) this.selectedIndex = 1;
|
|
||||||
if (comb) this.selectedIndex = 2;
|
|
||||||
if (notes) this.selectedIndex = 3;
|
|
||||||
}
|
|
||||||
this.scrolled = this.scrollview.verticalOffset != 0;
|
|
||||||
let scrollUp;
|
let scrollUp;
|
||||||
let y = args.scrollY;
|
let y = args.scrollY;
|
||||||
if (y) {
|
if (y) {
|
||||||
|
@ -373,20 +339,34 @@ export default {
|
||||||
this.scrollPos = Math.abs(y);
|
this.scrollPos = Math.abs(y);
|
||||||
let ab = this.appbar.translateY;
|
let ab = this.appbar.translateY;
|
||||||
if (!scrollUp && ab == 0)
|
if (!scrollUp && ab == 0)
|
||||||
this.animateInOut(
|
this.appbar.animate({
|
||||||
250,
|
translate: { x: 0, y: 64 },
|
||||||
false,
|
duration: 250,
|
||||||
(val) => (this.appbar.translateY = val * 64)
|
curve: CoreTypes.AnimationCurve.ease,
|
||||||
);
|
});
|
||||||
else if (scrollUp && ab == 64)
|
else if (scrollUp && ab == 64)
|
||||||
this.animateInOut(
|
this.appbar.animate({
|
||||||
250,
|
translate: { x: 0, y: 0 },
|
||||||
true,
|
duration: 250,
|
||||||
(val) => (this.appbar.translateY = val * 64)
|
curve: CoreTypes.AnimationCurve.ease,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
getTitleCount(title, type) {
|
||||||
|
let count = this.recipe[type].length;
|
||||||
|
let selected = null;
|
||||||
|
switch (title) {
|
||||||
|
case "ings":
|
||||||
|
selected = this.checked;
|
||||||
|
break;
|
||||||
|
case "inss":
|
||||||
|
selected = this.stepsDid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let text = selected ? ` (${selected}/${count})` : ` (${count})`;
|
||||||
|
return localize(title) + text;
|
||||||
|
},
|
||||||
changeYield() {
|
changeYield() {
|
||||||
this.$showModal(PromptDialog, {
|
this.$showModal(PromptDialog, {
|
||||||
props: {
|
props: {
|
||||||
|
@ -422,18 +402,17 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
showLastTried() {
|
showLastTried() {
|
||||||
this.showToast(
|
this.toast = `${localize("triedInfo")} ${this.niceDate(
|
||||||
`${localize("triedInfo")} ${this.niceDate(this.recipe.lastTried)}`,
|
this.recipe.lastTried
|
||||||
5,
|
)}`;
|
||||||
true
|
utils.timer(5, (val) => {
|
||||||
);
|
if (!val) this.toast = val;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
roundedQuantity(quantity) {
|
roundedQuantity(quantity) {
|
||||||
return Math.abs(
|
return Math.abs(
|
||||||
Math.round(
|
Math.round(
|
||||||
(quantity / this.recipe.yield.quantity) *
|
(quantity / this.recipe.yield.quantity) * this.tempYieldQuantity * 100
|
||||||
this.positiveYieldMultiplier *
|
|
||||||
100
|
|
||||||
) / 100
|
) / 100
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -485,37 +464,48 @@ export default {
|
||||||
recipe: this.recipe,
|
recipe: this.recipe,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
checkChange(obj, index) {
|
|
||||||
this.checks[index] = !this.checks[index];
|
|
||||||
obj.getChildAt(0).text = this.checks[index]
|
|
||||||
? this.icon.succ
|
|
||||||
: this.icon.done;
|
|
||||||
},
|
|
||||||
touchIngredient({ object, action }, index) {
|
touchIngredient({ object, action }, index) {
|
||||||
object.className = action.match(/down|move/)
|
object.className = action.match(/down|move/)
|
||||||
? "ingredient fade"
|
? "ingredient fade"
|
||||||
: "ingredient";
|
: "ingredient";
|
||||||
if (action == "up") this.checkChange(object, index);
|
if (action == "up") this.checkChange(object, index);
|
||||||
},
|
},
|
||||||
clearChecks() {
|
checkChange(obj, index) {
|
||||||
for (let i = 0; i < this.ingcon.getChildrenCount(); i++) {
|
this.checks[index] = !this.checks[index];
|
||||||
this.ingcon.getChildAt(i).getChildAt(0).text = this.icon.done;
|
if (this.checks[index]) {
|
||||||
|
this.checked++;
|
||||||
|
obj.getChildAt(0).text = this.icon.check;
|
||||||
|
} else {
|
||||||
|
this.checked--;
|
||||||
|
obj.getChildAt(0).text = this.icon.uncheck;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
stepDone(object) {
|
clearChecks() {
|
||||||
let a = object;
|
this.checked = 0;
|
||||||
a.className = a.className.includes("done")
|
this.checks = [];
|
||||||
? "instruction"
|
for (let i = 0; i < this.ingcon.getChildrenCount(); i++) {
|
||||||
: "instruction done";
|
this.ingcon.getChildAt(i).getChildAt(0).text = this.icon.uncheck;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
touchInstruction({ object, action }) {
|
touchInstruction({ object, action }) {
|
||||||
let hasDone = object.className.includes("done");
|
let hasDone = object.className.includes("done");
|
||||||
object.className = action.match(/down|move/)
|
object.className = action.match(/down|move/)
|
||||||
? `instruction fade ${hasDone ? "done" : ""}`
|
? `instruction ${hasDone ? "done" : "fade"}`
|
||||||
: `instruction ${hasDone ? "done" : ""}`;
|
: `instruction ${hasDone ? "done" : ""}`;
|
||||||
if (action == "up") this.stepDone(object);
|
if (action == "up") this.stepDone(object);
|
||||||
},
|
},
|
||||||
|
stepDone(object) {
|
||||||
|
let a = object;
|
||||||
|
if (a.className.includes("done")) {
|
||||||
|
a.className = "instruction";
|
||||||
|
this.stepsDid--;
|
||||||
|
} else {
|
||||||
|
a.className = "instruction done";
|
||||||
|
this.stepsDid++;
|
||||||
|
}
|
||||||
|
},
|
||||||
clearSteps() {
|
clearSteps() {
|
||||||
|
this.stepsDid = 0;
|
||||||
for (let i = 0; i < this.inscon.getChildrenCount(); i++) {
|
for (let i = 0; i < this.inscon.getChildrenCount(); i++) {
|
||||||
this.inscon.getChildAt(i).className = "instruction";
|
this.inscon.getChildAt(i).className = "instruction";
|
||||||
}
|
}
|
||||||
|
@ -536,12 +526,13 @@ export default {
|
||||||
viewCombination(combination) {
|
viewCombination(combination) {
|
||||||
this.scrollview.scrollToVerticalOffset(0, true);
|
this.scrollview.scrollToVerticalOffset(0, true);
|
||||||
this.recipe = this.recipes.filter((e) => e.id === combination)[0];
|
this.recipe = this.recipes.filter((e) => e.id === combination)[0];
|
||||||
this.recipe.ingredients.forEach((e) => this.checks.push(false));
|
|
||||||
this.clearSteps();
|
|
||||||
this.clearChecks();
|
this.clearChecks();
|
||||||
|
this.clearSteps();
|
||||||
|
this.recipe.ingredients.forEach(() => this.checks.push(false));
|
||||||
this.currentRecipeID = combination;
|
this.currentRecipeID = combination;
|
||||||
this.syncCombinations();
|
this.syncCombinations();
|
||||||
this.createNotes();
|
this.createNotes();
|
||||||
|
this.yieldMultiplier = this.recipe.yield.quantity;
|
||||||
this.recipe.tried && this.recipe.lastTried && this.showLastTried();
|
this.recipe.tried && this.recipe.lastTried && this.showLastTried();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -563,8 +554,6 @@ export default {
|
||||||
case "rec":
|
case "rec":
|
||||||
this.shareRecipe();
|
this.shareRecipe();
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -572,24 +561,24 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
shareRecipe() {
|
shareRecipe() {
|
||||||
let overview = `${this.recipe.title}\n\n${localize(
|
let overview = `${this.recipe.title}\n\n${localize("stars")}: ${
|
||||||
"Cuisine"
|
this.recipe.rating
|
||||||
)}: ${localize(this.recipe.cuisine)}\n${localize("Category")}: ${localize(
|
}\n${localize("cui")}: ${localize(this.recipe.cuisine)}\n${localize(
|
||||||
this.recipe.category
|
"cat"
|
||||||
)}\n${localize("ts")}: ${this.recipe.tags.join(", ")}\n${localize(
|
)}: ${localize(this.recipe.category)}\n${localize(
|
||||||
"stars"
|
"ts"
|
||||||
)}: ${this.recipe.rating}\n${localize("Difficulty level")}: ${localize(
|
)}: ${this.recipe.tags.join(", ")}\n${localize(
|
||||||
this.recipe.difficulty
|
"prepT"
|
||||||
)}\n${localize("Preparation time")}: ${this.formattedTime(
|
)}: ${this.formattedTime(this.recipe.prepTime)}\n${localize(
|
||||||
this.recipe.prepTime
|
"cookT"
|
||||||
)}\n${localize("Cooking time")}: ${this.formattedTime(
|
)}: ${this.formattedTime(this.recipe.cookTime)}\n${localize("yld")}: ${
|
||||||
this.recipe.cookTime
|
this.tempYieldQuantity
|
||||||
)}\n`;
|
} ${localize(this.recipe.yield.unit)}\n${localize(
|
||||||
|
"Difficulty level"
|
||||||
|
)}: ${localize(this.recipe.difficulty)}\n`;
|
||||||
let shareContent = overview;
|
let shareContent = overview;
|
||||||
if (this.recipe.ingredients.length) {
|
if (this.recipe.ingredients.length) {
|
||||||
let ingredients = `\n\n${localize("ings")} (${
|
let ingredients = `\n\n${localize("ings")}:\n\n`;
|
||||||
this.yieldMultiplier
|
|
||||||
} ${localize(this.recipe.yield.unit)}):\n\n`;
|
|
||||||
this.recipe.ingredients.forEach((e) => {
|
this.recipe.ingredients.forEach((e) => {
|
||||||
ingredients += `- ${
|
ingredients += `- ${
|
||||||
e.quantity
|
e.quantity
|
||||||
|
@ -603,19 +592,12 @@ export default {
|
||||||
shareContent += ingredients;
|
shareContent += ingredients;
|
||||||
}
|
}
|
||||||
if (this.recipe.instructions.length) {
|
if (this.recipe.instructions.length) {
|
||||||
let instructions = `\n\n${localize("Instructions")}:\n\n`;
|
let instructions = `\n\n${localize("inss")}:\n\n`;
|
||||||
this.recipe.instructions.forEach((e, i) => {
|
this.recipe.instructions.forEach((e, i) => {
|
||||||
instructions += `${i + 1}. ${e}\n\n`;
|
instructions += `${i + 1}. ${e}\n\n`;
|
||||||
});
|
});
|
||||||
shareContent += instructions;
|
shareContent += instructions;
|
||||||
}
|
}
|
||||||
if (this.recipe.notes.length) {
|
|
||||||
let notes = `\n${localize("nos")}:\n\n`;
|
|
||||||
this.recipe.notes.forEach((e, i) => {
|
|
||||||
notes += `${i + 1}. ${e}\n\n`;
|
|
||||||
});
|
|
||||||
shareContent += notes;
|
|
||||||
}
|
|
||||||
if (this.recipe.combinations.length) {
|
if (this.recipe.combinations.length) {
|
||||||
let combinations = `\n${localize("cmbs")}:\n\n`;
|
let combinations = `\n${localize("cmbs")}:\n\n`;
|
||||||
this.recipe.combinations.forEach((e, i) => {
|
this.recipe.combinations.forEach((e, i) => {
|
||||||
|
@ -623,24 +605,20 @@ export default {
|
||||||
});
|
});
|
||||||
shareContent += combinations;
|
shareContent += combinations;
|
||||||
}
|
}
|
||||||
|
if (this.recipe.notes.length) {
|
||||||
|
let notes = `\n${localize("nos")}:\n\n`;
|
||||||
|
this.recipe.notes.forEach((e, i) => {
|
||||||
|
notes += `${i + 1}. ${e}\n\n`;
|
||||||
|
});
|
||||||
|
shareContent += notes;
|
||||||
|
}
|
||||||
let sharenote = "\n" + localize("appCrd");
|
let sharenote = "\n" + localize("appCrd");
|
||||||
shareContent += sharenote;
|
shareContent += sharenote;
|
||||||
SocialShare.shareText(shareContent, "Share recipe using");
|
// SocialShare.shareText(shareContent, "Share recipe using");
|
||||||
|
utils.shareText(shareContent, "Share recipe using");
|
||||||
},
|
},
|
||||||
|
|
||||||
// DATA HANDLERS
|
// DATA HANDLERS
|
||||||
showToast(msg, dur, hl) {
|
|
||||||
clearInterval(toastTimer);
|
|
||||||
this.hlMsg = hl;
|
|
||||||
this.toast = msg;
|
|
||||||
toastTimer = setInterval(() => {
|
|
||||||
dur--;
|
|
||||||
if (dur == 0) {
|
|
||||||
this.toast = null;
|
|
||||||
clearInterval(toastTimer);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
},
|
|
||||||
toggle(key, setDate) {
|
toggle(key, setDate) {
|
||||||
this.toggleStateAction({
|
this.toggleStateAction({
|
||||||
id: this.currentRecipeID,
|
id: this.currentRecipeID,
|
||||||
|
@ -649,18 +627,6 @@ export default {
|
||||||
setDate,
|
setDate,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
toggleFavourite() {
|
|
||||||
this.recipe.isFavorite
|
|
||||||
? this.showToast(localize("unfavd"), 3)
|
|
||||||
: this.showToast(localize("favd"), 3);
|
|
||||||
this.toggle("isFavorite");
|
|
||||||
},
|
|
||||||
toggleTrylater() {
|
|
||||||
this.recipe.tried
|
|
||||||
? this.showToast(localize("aTry"), 3)
|
|
||||||
: this.showToast(localize("rmTry"), 3);
|
|
||||||
this.toggle("tried");
|
|
||||||
},
|
|
||||||
recipeTried() {
|
recipeTried() {
|
||||||
this.setRecipeAsTriedAction({
|
this.setRecipeAsTriedAction({
|
||||||
id: this.currentRecipeID,
|
id: this.currentRecipeID,
|
||||||
|
@ -724,32 +690,91 @@ export default {
|
||||||
getTags(tags) {
|
getTags(tags) {
|
||||||
return tags.join(" · ");
|
return tags.join(" · ");
|
||||||
},
|
},
|
||||||
|
hijackBackEvent() {
|
||||||
|
AndroidApplication.on(
|
||||||
|
AndroidApplication.activityBackPressedEvent,
|
||||||
|
this.backEvent
|
||||||
|
);
|
||||||
|
},
|
||||||
|
releaseBackEvent() {
|
||||||
|
AndroidApplication.off(
|
||||||
|
AndroidApplication.activityBackPressedEvent,
|
||||||
|
this.backEvent
|
||||||
|
);
|
||||||
|
},
|
||||||
|
backEvent(args) {
|
||||||
|
if (this.photoOpen) {
|
||||||
|
args.cancel = true;
|
||||||
|
this.closePhoto();
|
||||||
|
} else this.$navigateBack();
|
||||||
|
},
|
||||||
|
viewPhoto() {
|
||||||
|
this.imgZoom.initNativeView();
|
||||||
|
this.photoOpen = true;
|
||||||
|
this.hijackBackEvent();
|
||||||
|
let pv = this.imgZoom;
|
||||||
|
pv.visibility = "visible";
|
||||||
|
let sw = Screen.mainScreen.widthDIPs;
|
||||||
|
let sh = Screen.mainScreen.heightDIPs;
|
||||||
|
pv.animate({
|
||||||
|
opacity: 1,
|
||||||
|
duration: 50,
|
||||||
|
})
|
||||||
|
.then(() =>
|
||||||
|
pv.animate({
|
||||||
|
width: sw,
|
||||||
|
height: sw,
|
||||||
|
translate: { x: 112 - sw, y: (sh - sw) / 3 },
|
||||||
|
duration: 250,
|
||||||
|
curve: CoreTypes.AnimationCurve.ease,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(() =>
|
||||||
|
pv.animate({
|
||||||
|
height: sh,
|
||||||
|
translate: { x: -sw + 112, y: -((sh - sw) / 6) },
|
||||||
|
duration: 250,
|
||||||
|
curve: CoreTypes.AnimationCurve.ease,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
closePhoto() {
|
||||||
|
let pv = this.imgZoom;
|
||||||
|
let sw = Screen.mainScreen.widthDIPs;
|
||||||
|
let sh = Screen.mainScreen.heightDIPs;
|
||||||
|
pv.animate({
|
||||||
|
width: sw,
|
||||||
|
height: sw,
|
||||||
|
translate: { x: 112 - sw, y: (sh - sw) / 3 },
|
||||||
|
duration: 250,
|
||||||
|
curve: CoreTypes.AnimationCurve.ease,
|
||||||
|
})
|
||||||
|
.then(() =>
|
||||||
|
pv.animate({
|
||||||
|
width: 96,
|
||||||
|
height: 96,
|
||||||
|
translate: { x: 0, y: 0 },
|
||||||
|
duration: 250,
|
||||||
|
curve: CoreTypes.AnimationCurve.ease,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(() =>
|
||||||
|
pv.animate({
|
||||||
|
opacity: 0,
|
||||||
|
duration: 50,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
pv.visibility = "collapsed";
|
||||||
|
this.photoOpen = false;
|
||||||
|
this.releaseBackEvent();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
navigateBack() {
|
||||||
|
this.photoOpen ? this.closePhoto() : this.$navigateBack();
|
||||||
|
},
|
||||||
|
|
||||||
//HELPERS
|
//HELPERS
|
||||||
touchSelector({ object, action }, index, id) {
|
|
||||||
let selected = this.selectedIndex == index;
|
|
||||||
object.className = action.match(/down|move/)
|
|
||||||
? `segment ${selected ? "select" : "fade"}`
|
|
||||||
: `segment ${selected && "select"}`;
|
|
||||||
if (action == "up") this.scrollToId(index, id);
|
|
||||||
},
|
|
||||||
scrollToId(index, id) {
|
|
||||||
this.scrollingToId = true;
|
|
||||||
if (index != this.selectedIndex)
|
|
||||||
this.scrollview.scrollToVerticalOffset(
|
|
||||||
this.scrollview.getViewById(id).getLocationRelativeTo(this.scrollview)
|
|
||||||
.y + this.scrollview.verticalOffset,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
clearInterval(scrollTimer);
|
|
||||||
scrollTimer = setInterval(() => {
|
|
||||||
if (this.selectedIndex == index) {
|
|
||||||
this.scrollingToId = false;
|
|
||||||
clearInterval(scrollTimer);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
this.selectedIndex = index;
|
|
||||||
},
|
|
||||||
touchYield({ object, action }) {
|
touchYield({ object, action }) {
|
||||||
object.className = action.match(/down|move/)
|
object.className = action.match(/down|move/)
|
||||||
? "value clickable fade"
|
? "value clickable fade"
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<Page @loaded="onPageLoad" backgroundColor="transparent" :class="appTheme">
|
<Page
|
||||||
|
@loaded="transparentPage"
|
||||||
|
backgroundColor="transparent"
|
||||||
|
:class="appTheme"
|
||||||
|
>
|
||||||
<GridLayout
|
<GridLayout
|
||||||
columns="*"
|
columns="*"
|
||||||
:rows="`auto, auto, ${stretch ? '*' : 'auto'}, auto`"
|
:rows="`auto, auto, ${stretch ? '*' : 'auto'}, auto`"
|
||||||
|
@ -9,7 +13,6 @@
|
||||||
<ListView
|
<ListView
|
||||||
rowHeight="48"
|
rowHeight="48"
|
||||||
row="2"
|
row="2"
|
||||||
@loaded="listViewLoad"
|
|
||||||
for="item in newList"
|
for="item in newList"
|
||||||
:height="stretch ? '100%' : listHeight"
|
:height="stretch ? '100%' : listHeight"
|
||||||
>
|
>
|
||||||
|
@ -63,16 +66,6 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(["removeListItemAction"]),
|
...mapActions(["removeListItemAction"]),
|
||||||
onPageLoad(args) {
|
|
||||||
args.object._dialogFragment
|
|
||||||
.getDialog()
|
|
||||||
.getWindow()
|
|
||||||
.setBackgroundDrawable(
|
|
||||||
new android.graphics.drawable.ColorDrawable(
|
|
||||||
android.graphics.Color.TRANSPARENT
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
localized(item) {
|
localized(item) {
|
||||||
if (this.title !== "lang") return localize(item);
|
if (this.title !== "lang") return localize(item);
|
||||||
else return item;
|
else return item;
|
||||||
|
@ -118,7 +111,6 @@ export default {
|
||||||
case "Unit":
|
case "Unit":
|
||||||
removeListItem("unit", "units", "rmUInfo");
|
removeListItem("unit", "units", "rmUInfo");
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
touch({ object, action }) {
|
touch({ object, action }) {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<Page @loaded="onPageLoad" backgroundColor="transparent" :class="appTheme">
|
<Page
|
||||||
|
@loaded="transparentPage"
|
||||||
|
backgroundColor="transparent"
|
||||||
|
:class="appTheme"
|
||||||
|
>
|
||||||
<GridLayout columns="*" rows="auto, auto, *, auto" class="modal">
|
<GridLayout columns="*" rows="auto, auto, *, auto" class="modal">
|
||||||
<Label class="title" :text="title | L" />
|
<Label class="title" :text="title | L" />
|
||||||
<StackLayout
|
<StackLayout
|
||||||
|
@ -9,11 +13,11 @@
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
class="modalInput"
|
class="modalInput"
|
||||||
:hint="'Search' | L"
|
:hint="'ser' | L"
|
||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
/>
|
/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<ListView row="2" for="recipe in filteredRecipes" @loaded="listViewLoad">
|
<ListView row="2" for="recipe in filteredRecipes">
|
||||||
<v-template>
|
<v-template>
|
||||||
<Label
|
<Label
|
||||||
class="listItem"
|
class="listItem"
|
||||||
|
@ -79,16 +83,6 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onPageLoad(args) {
|
|
||||||
args.object._dialogFragment
|
|
||||||
.getDialog()
|
|
||||||
.getWindow()
|
|
||||||
.setBackgroundDrawable(
|
|
||||||
new android.graphics.drawable.ColorDrawable(
|
|
||||||
android.graphics.Color.TRANSPARENT
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
tapAction(recipe) {
|
tapAction(recipe) {
|
||||||
this.$modal.close(recipe.id);
|
this.$modal.close(recipe.id);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<Page @loaded="onPageLoad" backgroundColor="transparent" :class="appTheme">
|
<Page @loaded="transparentPage" backgroundColor="transparent" :class="appTheme">
|
||||||
<GridLayout rows="auto, auto, auto" class="modal">
|
<GridLayout rows="auto, auto, auto" class="modal">
|
||||||
<Label class="title" :text="title | L" />
|
<Label class="title" :text="title | L" />
|
||||||
<Label
|
<Label
|
||||||
|
@ -47,17 +47,5 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["icon", "appTheme"]),
|
...mapState(["icon", "appTheme"]),
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
onPageLoad(args) {
|
|
||||||
args.object._dialogFragment
|
|
||||||
.getDialog()
|
|
||||||
.getWindow()
|
|
||||||
.setBackgroundDrawable(
|
|
||||||
new android.graphics.drawable.ColorDrawable(
|
|
||||||
android.graphics.Color.TRANSPARENT
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -20,12 +20,7 @@
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<ListView
|
<ListView row="2" class="options-list" for="item in filterList">
|
||||||
row="2"
|
|
||||||
class="options-list"
|
|
||||||
@loaded="listViewLoad"
|
|
||||||
for="item in filterList"
|
|
||||||
>
|
|
||||||
<v-template>
|
<v-template>
|
||||||
<Label
|
<Label
|
||||||
class="listItem"
|
class="listItem"
|
||||||
|
@ -98,10 +93,8 @@ export default {
|
||||||
switch (this.filterType) {
|
switch (this.filterType) {
|
||||||
case "cuisine":
|
case "cuisine":
|
||||||
return arr.slice(0, -2);
|
return arr.slice(0, -2);
|
||||||
break;
|
|
||||||
case "category":
|
case "category":
|
||||||
return arr.slice(0, -1);
|
return arr.slice(0, -1);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
},
|
},
|
||||||
|
@ -109,13 +102,10 @@ export default {
|
||||||
switch (this.filterType) {
|
switch (this.filterType) {
|
||||||
case "cuisine":
|
case "cuisine":
|
||||||
return this.cuisineList;
|
return this.cuisineList;
|
||||||
break;
|
|
||||||
case "category":
|
case "category":
|
||||||
return this.categoryList;
|
return this.categoryList;
|
||||||
break;
|
|
||||||
case "tag":
|
case "tag":
|
||||||
return this.tagList;
|
return this.tagList;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cuisineList() {
|
cuisineList() {
|
||||||
|
@ -181,14 +171,7 @@ export default {
|
||||||
"clearFilter",
|
"clearFilter",
|
||||||
]),
|
]),
|
||||||
onPageLoad(args) {
|
onPageLoad(args) {
|
||||||
args.object._dialogFragment
|
this.transparentPage(args);
|
||||||
.getDialog()
|
|
||||||
.getWindow()
|
|
||||||
.setBackgroundDrawable(
|
|
||||||
new android.graphics.drawable.ColorDrawable(
|
|
||||||
android.graphics.Color.TRANSPARENT
|
|
||||||
)
|
|
||||||
);
|
|
||||||
this.localCuisine = this.selectedCuisine;
|
this.localCuisine = this.selectedCuisine;
|
||||||
this.localCategory = this.selectedCategory;
|
this.localCategory = this.selectedCategory;
|
||||||
this.localTag = this.selectedTag;
|
this.localTag = this.selectedTag;
|
||||||
|
@ -209,8 +192,6 @@ export default {
|
||||||
case "category":
|
case "category":
|
||||||
this.localTag = null;
|
this.localTag = null;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scrollToRight() {
|
scrollToRight() {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<Page @loaded="onPageLoad" backgroundColor="transparent" :class="appTheme">
|
<Page
|
||||||
|
@loaded="transparentPage"
|
||||||
|
backgroundColor="transparent"
|
||||||
|
:class="appTheme"
|
||||||
|
>
|
||||||
<GridLayout rows="auto, auto, auto" class="modal">
|
<GridLayout rows="auto, auto, auto" class="modal">
|
||||||
<Label class="title" :text="title | L" />
|
<Label class="title" :text="title | L" />
|
||||||
<StackLayout
|
<StackLayout
|
||||||
|
@ -9,12 +13,14 @@
|
||||||
horizontalAlignment="center"
|
horizontalAlignment="center"
|
||||||
>
|
>
|
||||||
<ListPicker
|
<ListPicker
|
||||||
|
@loaded="onLPLoad"
|
||||||
ref="hrPicker"
|
ref="hrPicker"
|
||||||
:items="hrsList"
|
:items="hrsList"
|
||||||
:selectedIndex="hrIndex"
|
:selectedIndex="hrIndex"
|
||||||
@selectedIndexChange="setHrs"
|
@selectedIndexChange="setHrs"
|
||||||
></ListPicker>
|
></ListPicker>
|
||||||
<ListPicker
|
<ListPicker
|
||||||
|
@loaded="onLPLoad"
|
||||||
ref="minPicker"
|
ref="minPicker"
|
||||||
:items="minsList"
|
:items="minsList"
|
||||||
:selectedIndex="minIndex"
|
:selectedIndex="minIndex"
|
||||||
|
@ -40,7 +46,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Application } from "@nativescript/core";
|
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import { localize } from "@nativescript/localize";
|
import { localize } from "@nativescript/localize";
|
||||||
export default {
|
export default {
|
||||||
|
@ -81,15 +86,8 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onPageLoad(args) {
|
onLPLoad({ object }) {
|
||||||
args.object._dialogFragment
|
object.android.setWrapSelectorWheel(true);
|
||||||
.getDialog()
|
|
||||||
.getWindow()
|
|
||||||
.setBackgroundDrawable(
|
|
||||||
new android.graphics.drawable.ColorDrawable(
|
|
||||||
android.graphics.Color.TRANSPARENT
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
setHrs(args) {
|
setHrs(args) {
|
||||||
let hr = "0" + this.hrs[args.object.selectedIndex];
|
let hr = "0" + this.hrs[args.object.selectedIndex];
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<Page @loaded="onPageLoad" backgroundColor="transparent" :class="appTheme">
|
<Page
|
||||||
|
@loaded="transparentPage"
|
||||||
|
backgroundColor="transparent"
|
||||||
|
:class="appTheme"
|
||||||
|
>
|
||||||
<GridLayout rows="auto, auto, auto" class="modal">
|
<GridLayout rows="auto, auto, auto" class="modal">
|
||||||
<Label class="title" :text="title | L" />
|
<Label class="title" :text="title | L" />
|
||||||
<StackLayout row="1" class="input">
|
<StackLayout row="1" class="input">
|
||||||
|
@ -43,16 +47,6 @@ export default {
|
||||||
...mapState(["icon", "appTheme"]),
|
...mapState(["icon", "appTheme"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onPageLoad(args) {
|
|
||||||
args.object._dialogFragment
|
|
||||||
.getDialog()
|
|
||||||
.getWindow()
|
|
||||||
.setBackgroundDrawable(
|
|
||||||
new android.graphics.drawable.ColorDrawable(
|
|
||||||
android.graphics.Color.TRANSPARENT
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
focusField({ object }) {
|
focusField({ object }) {
|
||||||
let a = this.placeholder;
|
let a = this.placeholder;
|
||||||
typeof a == "number"
|
typeof a == "number"
|
||||||
|
|
Binary file not shown.
|
@ -271,7 +271,6 @@
|
||||||
"buInc": "Malformed or corrupt backup file",
|
"buInc": "Malformed or corrupt backup file",
|
||||||
"buEmp": "The backup file is empty",
|
"buEmp": "The backup file is empty",
|
||||||
"buMod": "The backup file was modified elsewhere",
|
"buMod": "The backup file was modified elsewhere",
|
||||||
"buSuc": "Backup saved successfully to the Download folder",
|
|
||||||
"invFile": "Invalid file",
|
"invFile": "Invalid file",
|
||||||
"donate": "Donate",
|
"donate": "Donate",
|
||||||
"appInfo": "EnRecipes is an open source, privacy-friendly digital cookbook that lets you create, manage and share your recipes",
|
"appInfo": "EnRecipes is an open source, privacy-friendly digital cookbook that lets you create, manage and share your recipes",
|
||||||
|
@ -308,5 +307,7 @@
|
||||||
"apply": "APPLY",
|
"apply": "APPLY",
|
||||||
"fltr": "Filter",
|
"fltr": "Filter",
|
||||||
"yld": "Yield",
|
"yld": "Yield",
|
||||||
"swm": "Start week on Monday"
|
"swm": "Start week on Monday",
|
||||||
|
"expip": "Export in progress",
|
||||||
|
"impip": "Import in progress"
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,15 @@ import store from './store'
|
||||||
import CollectionView from '@nativescript-community/ui-collectionview/vue'
|
import CollectionView from '@nativescript-community/ui-collectionview/vue'
|
||||||
Vue.use(CollectionView)
|
Vue.use(CollectionView)
|
||||||
|
|
||||||
|
Vue.registerElement(
|
||||||
|
'ImageZoom',
|
||||||
|
() => require('@triniwiz/nativescript-image-zoom').ImageZoom
|
||||||
|
)
|
||||||
|
|
||||||
import { lvMixin } from './shared/mixins.js'
|
import { lvMixin } from './shared/mixins.js'
|
||||||
Vue.mixin(lvMixin)
|
Vue.mixin(lvMixin)
|
||||||
|
|
||||||
Vue.config.silent = TNS_ENV === 'production'
|
Vue.config.silent = false
|
||||||
|
|
||||||
Vue.filter('L', localize)
|
Vue.filter('L', localize)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,9 @@ android {
|
||||||
versionCode 7
|
versionCode 7
|
||||||
versionName '1.3.2'
|
versionName '1.3.2'
|
||||||
applicationId 'com.vishnuraghav.enrecipes'
|
applicationId 'com.vishnuraghav.enrecipes'
|
||||||
minSdkVersion 22
|
minSdkVersion 23
|
||||||
|
targetSdkVersion 30
|
||||||
|
compileSdkVersion 30
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters.clear()
|
abiFilters.clear()
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="__PACKAGE__" android:versionCode="10000" android:versionName="1.0">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="__PACKAGE__" android:versionCode="10000" android:versionName="1.0">
|
||||||
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" />
|
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" tools:node="remove" />
|
<uses-permission android:name="android.permission.CAMERA" tools:node="remove" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<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: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" android:requestLegacyExternalStorage="true">
|
<application 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" android:preserveLegacyExternalStorage="true">
|
||||||
<activity android:name="com.tns.NativeScriptActivity" android:label="@string/title_activity_kimera" 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: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>
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
export const lvMixin = {
|
export const lvMixin = {
|
||||||
methods: {
|
methods: {
|
||||||
listViewLoad(args) {
|
transparentPage({ object }) {
|
||||||
let e = args.object.android
|
object._dialogFragment
|
||||||
e.setSelector(new android.graphics.drawable.StateListDrawable())
|
.getDialog()
|
||||||
e.setDivider(null)
|
.getWindow()
|
||||||
e.setDividerHeight(0)
|
.setBackgroundDrawable(
|
||||||
},
|
new android.graphics.drawable.ColorDrawable(
|
||||||
animateInOut(dur, rev, draw) {
|
android.graphics.Color.TRANSPARENT
|
||||||
const start = Date.now()
|
)
|
||||||
let timer = setInterval(() => {
|
)
|
||||||
const passed = Date.now() - start
|
|
||||||
let val = passed / dur
|
|
||||||
if (val > 1) val = 1
|
|
||||||
draw(rev ? 1 - val : val)
|
|
||||||
if (val === 1) clearInterval(timer)
|
|
||||||
}, 17) // 1000ms/60fps=16.66ms => 17ms
|
|
||||||
},
|
},
|
||||||
|
// animateInOut(dur, rev, draw) {
|
||||||
|
// const start = Date.now()
|
||||||
|
// let timer = setInterval(() => {
|
||||||
|
// const passed = Date.now() - start
|
||||||
|
// let val = passed / dur
|
||||||
|
// if (val > 1) val = 1
|
||||||
|
// draw(rev ? 1 - val : val)
|
||||||
|
// if (val === 1) clearInterval(timer)
|
||||||
|
// }, 17) // 1000ms/60fps=16.66ms => 17ms
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,7 @@ export const timer = (dur, callback) => {
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FOLDER PICKER
|
function callIntent(context, intent, msg, pickerType) {
|
||||||
function callIntent(context, intent, pickerType) {
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const onEvent = function(e) {
|
const onEvent = function(e) {
|
||||||
if (e.requestCode === pickerType) {
|
if (e.requestCode === pickerType) {
|
||||||
|
@ -72,215 +71,63 @@ function callIntent(context, intent, pickerType) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Application.android.once(AndroidApplication.activityResultEvent, onEvent)
|
Application.android.once(AndroidApplication.activityResultEvent, onEvent)
|
||||||
context.startActivityForResult(intent, pickerType)
|
context.startActivityForResult(
|
||||||
|
android.content.Intent.createChooser(intent, msg),
|
||||||
|
pickerType
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// IMAGE PICKER
|
||||||
|
export const getRecipePhoto = () => {
|
||||||
|
const context =
|
||||||
|
Application.android.foregroundActivity || Application.android.startActivity
|
||||||
|
const DIR_CODE = Math.round(Math.random() * 10000)
|
||||||
|
const intent = new android.content.Intent(
|
||||||
|
android.content.Intent.ACTION_GET_CONTENT
|
||||||
|
)
|
||||||
|
intent.setType('image/*')
|
||||||
|
return callIntent(context, intent, 'Select photo', DIR_CODE).then((res) => {
|
||||||
|
if (res.resultCode === android.app.Activity.RESULT_OK)
|
||||||
|
if (res.intent != null && res.intent.getData())
|
||||||
|
return res.intent.getData()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export const copyPhotoToCache = (uri, filepath) => {
|
||||||
|
const ContentResolver = Application.android.nativeApp.getContentResolver()
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const inputStream = ContentResolver.openInputStream(uri)
|
||||||
|
const input = new java.io.BufferedInputStream(inputStream)
|
||||||
|
let size = input.available()
|
||||||
|
let buffer = Array.create('byte', size)
|
||||||
|
const output = new java.io.BufferedOutputStream(
|
||||||
|
new java.io.FileOutputStream(filepath)
|
||||||
|
)
|
||||||
|
input.read(buffer)
|
||||||
|
do {
|
||||||
|
output.write(buffer)
|
||||||
|
} while (input.read(buffer) != -1)
|
||||||
|
input.close()
|
||||||
|
output.close()
|
||||||
|
resolve(filepath)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//SOURCE: https://github.com/NativeScript/nativescript-imagepicker/blob/bc9209dd7d773eb437ff812f07873c72c789d572/src/imagepicker.android.ts
|
// BACKUP FOLDER PICKER
|
||||||
|
export const getBackupFolder = () => {
|
||||||
function getDataColumn(uri, selection, selectionArgs, isDownload) {
|
|
||||||
const ContentResolver = Application.android.nativeApp.getContentResolver()
|
|
||||||
let cursor = null
|
|
||||||
let filePath
|
|
||||||
if (isDownload) {
|
|
||||||
let columns = ['_display_name']
|
|
||||||
try {
|
|
||||||
cursor = ContentResolver.query(
|
|
||||||
uri,
|
|
||||||
columns,
|
|
||||||
selection,
|
|
||||||
selectionArgs,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
let column_index = cursor.getColumnIndexOrThrow(columns[0])
|
|
||||||
filePath = cursor.getString(column_index)
|
|
||||||
if (filePath) {
|
|
||||||
const dl = android.os.Environment.getExternalStoragePublicDirectory(
|
|
||||||
android.os.Environment.DIRECTORY_DOWNLOADS
|
|
||||||
)
|
|
||||||
filePath = `${dl}/${filePath}`
|
|
||||||
return filePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
} finally {
|
|
||||||
if (cursor) {
|
|
||||||
cursor.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let columns = [android.provider.MediaStore.MediaColumns.DATA]
|
|
||||||
let filePath
|
|
||||||
|
|
||||||
try {
|
|
||||||
cursor = ContentResolver.query(
|
|
||||||
uri,
|
|
||||||
columns,
|
|
||||||
selection,
|
|
||||||
selectionArgs,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
let column_index = cursor.getColumnIndexOrThrow(columns[0])
|
|
||||||
filePath = cursor.getString(column_index)
|
|
||||||
if (filePath) {
|
|
||||||
return filePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
} finally {
|
|
||||||
if (cursor) {
|
|
||||||
cursor.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
function getTreeUri(uri) {
|
|
||||||
const DocumentsContract = android.provider.DocumentsContract
|
|
||||||
const ContentResolver = Application.android.nativeApp.getContentResolver()
|
|
||||||
if (DocumentsContract.isTreeUri(uri)) {
|
|
||||||
let docId, id, type
|
|
||||||
let contentUri = null
|
|
||||||
if ('com.android.externalstorage.documents' === uri.getAuthority()) {
|
|
||||||
docId = DocumentsContract.getTreeDocumentId(uri).split(':')
|
|
||||||
type = docId[0]
|
|
||||||
id = docId[1]
|
|
||||||
|
|
||||||
if ('primary' === type.toLowerCase())
|
|
||||||
return android.os.Environment.getExternalStorageDirectory() + '/' + id
|
|
||||||
else {
|
|
||||||
ContentResolver.takePersistableUriPermission(
|
|
||||||
uri,
|
|
||||||
android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION |
|
|
||||||
android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|
||||||
)
|
|
||||||
const externalMediaDirs = Application.android.context.getExternalMediaDirs()
|
|
||||||
if (externalMediaDirs.length > 1) {
|
|
||||||
let filePath = externalMediaDirs[1].getAbsolutePath()
|
|
||||||
filePath = filePath.substring(0, filePath.indexOf('Android')) + id
|
|
||||||
return filePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// DownloadsProvider
|
|
||||||
else if (
|
|
||||||
'com.android.providers.downloads.documents' === uri.getAuthority()
|
|
||||||
) {
|
|
||||||
return getDataColumn(uri, null, null, true)
|
|
||||||
}
|
|
||||||
// MediaProvider
|
|
||||||
else if ('com.android.providers.media.documents' === uri.getAuthority()) {
|
|
||||||
docId = DocumentsContract.getTreeDocumentId(uri).split(':')
|
|
||||||
type = docId[0]
|
|
||||||
id = docId[1]
|
|
||||||
|
|
||||||
if ('image' === type)
|
|
||||||
contentUri =
|
|
||||||
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
|
||||||
else if ('video' === type)
|
|
||||||
contentUri =
|
|
||||||
android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
|
||||||
else if ('audio' === type)
|
|
||||||
contentUri =
|
|
||||||
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
|
||||||
|
|
||||||
let selection = '_id=?'
|
|
||||||
let selectionArgs = [id]
|
|
||||||
|
|
||||||
return getDataColumn(contentUri, selection, selectionArgs, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
function getFileUri(uri) {
|
|
||||||
const DocumentsContract = android.provider.DocumentsContract
|
|
||||||
const ContentResolver = Application.android.nativeApp.getContentResolver()
|
|
||||||
if (DocumentsContract.isDocumentUri(Application.android.context, uri)) {
|
|
||||||
let docId, id, type
|
|
||||||
let contentUri = null
|
|
||||||
if ('com.android.externalstorage.documents' === uri.getAuthority()) {
|
|
||||||
docId = DocumentsContract.getDocumentId(uri).split(':')
|
|
||||||
type = docId[0]
|
|
||||||
id = docId[1]
|
|
||||||
|
|
||||||
if ('primary' === type.toLowerCase())
|
|
||||||
return android.os.Environment.getExternalStorageDirectory() + '/' + id
|
|
||||||
else {
|
|
||||||
ContentResolver.takePersistableUriPermission(
|
|
||||||
uri,
|
|
||||||
android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION |
|
|
||||||
android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|
||||||
)
|
|
||||||
const externalMediaDirs = Application.android.context.getExternalMediaDirs()
|
|
||||||
if (externalMediaDirs.length > 1) {
|
|
||||||
let filePath = externalMediaDirs[1].getAbsolutePath()
|
|
||||||
filePath = filePath.substring(0, filePath.indexOf('Android')) + id
|
|
||||||
return filePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// DownloadsProvider
|
|
||||||
else if (
|
|
||||||
'com.android.providers.downloads.documents' === uri.getAuthority()
|
|
||||||
) {
|
|
||||||
return getDataColumn(uri, null, null, true)
|
|
||||||
}
|
|
||||||
// MediaProvider
|
|
||||||
else if ('com.android.providers.media.documents' === uri.getAuthority()) {
|
|
||||||
docId = DocumentsContract.getDocumentId(uri).split(':')
|
|
||||||
type = docId[0]
|
|
||||||
id = docId[1]
|
|
||||||
|
|
||||||
if ('image' === type)
|
|
||||||
contentUri =
|
|
||||||
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
|
||||||
else if ('video' === type)
|
|
||||||
contentUri =
|
|
||||||
android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
|
||||||
else if ('audio' === type)
|
|
||||||
contentUri =
|
|
||||||
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
|
||||||
|
|
||||||
let selection = '_id=?'
|
|
||||||
let selectionArgs = [id]
|
|
||||||
|
|
||||||
return getDataColumn(contentUri, selection, selectionArgs, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export const pickFolder = () => {
|
|
||||||
const context =
|
const context =
|
||||||
Application.android.foregroundActivity || Application.android.startActivity
|
Application.android.foregroundActivity || Application.android.startActivity
|
||||||
const DIR_CODE = Math.round(Math.random() * 10000)
|
const DIR_CODE = Math.round(Math.random() * 10000)
|
||||||
const intent = new android.content.Intent(
|
const intent = new android.content.Intent(
|
||||||
android.content.Intent.ACTION_OPEN_DOCUMENT_TREE
|
android.content.Intent.ACTION_OPEN_DOCUMENT_TREE
|
||||||
)
|
)
|
||||||
intent.addCategory(android.content.Intent.CATEGORY_DEFAULT)
|
return callIntent(context, intent, 'Select folder', DIR_CODE).then((res) => {
|
||||||
return callIntent(context, intent, DIR_CODE).then((res) => {
|
|
||||||
if (res.resultCode === android.app.Activity.RESULT_OK)
|
if (res.resultCode === android.app.Activity.RESULT_OK)
|
||||||
if (res.intent != null && res.intent.getData())
|
if (res.intent != null && res.intent.getData())
|
||||||
return getTreeUri(res.intent.getData())
|
return res.intent.getData()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function callBackupIntent(context, intent, pickerType) {
|
// BACKUP FILE PICKER
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const onEvent = function(e) {
|
|
||||||
if (e.requestCode === pickerType) {
|
|
||||||
resolve(e)
|
|
||||||
Application.android.off(AndroidApplication.activityResultEvent, onEvent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Application.android.once(AndroidApplication.activityResultEvent, onEvent)
|
|
||||||
context.startActivityForResult(intent, pickerType)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
export const getBackupFile = () => {
|
export const getBackupFile = () => {
|
||||||
const context =
|
const context =
|
||||||
Application.android.foregroundActivity || Application.android.startActivity
|
Application.android.foregroundActivity || Application.android.startActivity
|
||||||
|
@ -290,13 +137,118 @@ export const getBackupFile = () => {
|
||||||
)
|
)
|
||||||
intent.addCategory(android.content.Intent.CATEGORY_OPENABLE)
|
intent.addCategory(android.content.Intent.CATEGORY_OPENABLE)
|
||||||
intent.setType('application/zip')
|
intent.setType('application/zip')
|
||||||
// intent.setFlags(android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
return callIntent(context, intent, 'Select file to import', DIR_CODE).then(
|
||||||
return callBackupIntent(context, intent, DIR_CODE).then((res) => {
|
(res) => {
|
||||||
if (res.resultCode === android.app.Activity.RESULT_OK) {
|
if (res.resultCode === android.app.Activity.RESULT_OK) {
|
||||||
if (res.intent != null && res.intent.getData()) {
|
if (res.intent != null && res.intent.getData())
|
||||||
// return getFileUri(res.intent.getData())
|
|
||||||
return res.intent.getData()
|
return res.intent.getData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZIP OPERATIONS
|
||||||
|
export class Zip {
|
||||||
|
static getSubFiles(src, isRootFolder) {
|
||||||
|
const fileList = new java.util.ArrayList()
|
||||||
|
const sourceFile = new java.io.File(src)
|
||||||
|
let tempList = sourceFile.listFiles()
|
||||||
|
for (let i = 0; i < tempList.length; i++) {
|
||||||
|
if (tempList[i].isFile()) {
|
||||||
|
fileList.add(tempList[i])
|
||||||
|
}
|
||||||
|
if (tempList[i].isDirectory()) {
|
||||||
|
if (isRootFolder) {
|
||||||
|
fileList.add(tempList[i])
|
||||||
|
}
|
||||||
|
fileList.addAll(Zip.getSubFiles(tempList[i].getAbsolutePath()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileList
|
||||||
|
}
|
||||||
|
static zip(src, destUri, filename) {
|
||||||
|
const ContentResolver = Application.android.nativeApp.getContentResolver()
|
||||||
|
const parsedUri = new android.net.Uri.parse(destUri)
|
||||||
|
const uri = new androidx.documentfile.provider.DocumentFile.fromTreeUri(
|
||||||
|
Application.android.context,
|
||||||
|
parsedUri
|
||||||
|
)
|
||||||
|
.createFile('application/zip', filename)
|
||||||
|
.getUri()
|
||||||
|
const outputStream = ContentResolver.openOutputStream(uri)
|
||||||
|
const zipOutputStream = new java.util.zip.ZipOutputStream(outputStream)
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const sourceFiles = Zip.getSubFiles(src, true)
|
||||||
|
for (let i = 0; i < sourceFiles.size(); i++) {
|
||||||
|
let len
|
||||||
|
let buffer = Array.create('byte', 4096)
|
||||||
|
let file = sourceFiles.get(i)
|
||||||
|
let entry = new java.util.zip.ZipEntry(
|
||||||
|
'EnRecipes/' +
|
||||||
|
new java.io.File(src)
|
||||||
|
.toURI()
|
||||||
|
.relativize(file.toURI())
|
||||||
|
.getPath()
|
||||||
|
)
|
||||||
|
zipOutputStream.putNextEntry(entry)
|
||||||
|
if (!file.isDirectory()) {
|
||||||
|
let inputStream = new java.io.FileInputStream(file)
|
||||||
|
while ((len = inputStream.read(buffer)) != -1) {
|
||||||
|
zipOutputStream.write(buffer, 0, len)
|
||||||
|
}
|
||||||
|
inputStream.close()
|
||||||
|
}
|
||||||
|
zipOutputStream.closeEntry()
|
||||||
|
}
|
||||||
|
zipOutputStream.close()
|
||||||
|
resolve(uri)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
static unzip(uri, dest) {
|
||||||
|
const ContentResolver = Application.android.nativeApp.getContentResolver()
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const inputStream = ContentResolver.openInputStream(uri)
|
||||||
|
const zipInputStream = new java.util.zip.ZipInputStream(inputStream)
|
||||||
|
let len, filepath
|
||||||
|
let buffer = Array.create('byte', 4096)
|
||||||
|
let entry = zipInputStream.getNextEntry()
|
||||||
|
filepath = dest + '/' + entry.getName()
|
||||||
|
while (entry != null) {
|
||||||
|
filepath = dest + '/' + entry.getName()
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
const output = new java.io.BufferedOutputStream(
|
||||||
|
new java.io.FileOutputStream(filepath)
|
||||||
|
)
|
||||||
|
while ((len = zipInputStream.read(buffer)) != -1) {
|
||||||
|
output.write(buffer, 0, len)
|
||||||
|
}
|
||||||
|
output.close()
|
||||||
|
} else {
|
||||||
|
let dir = new java.io.File(filepath)
|
||||||
|
dir.mkdirs()
|
||||||
|
}
|
||||||
|
zipInputStream.closeEntry()
|
||||||
|
entry = zipInputStream.getNextEntry()
|
||||||
|
}
|
||||||
|
zipInputStream.close()
|
||||||
|
resolve(dest)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHARE OPERATIONS
|
||||||
|
|
||||||
|
function share(intent, subject) {
|
||||||
|
const context = Application.android.context
|
||||||
|
const shareIntent = android.content.Intent.createChooser(intent, subject)
|
||||||
|
shareIntent.setFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
context.startActivity(shareIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const shareText = (text, subject) => {
|
||||||
|
const intent = new android.context.Intent(android.content.Intent.ACTION_SEND)
|
||||||
|
intent.setType('text/plain')
|
||||||
|
intent.putExtra(android.context.Intent.EXTRA_TEXT, text)
|
||||||
|
share(intent, subject)
|
||||||
}
|
}
|
||||||
|
|
142
app/store.js
142
app/store.js
|
@ -160,72 +160,61 @@ export default new Vuex.Store({
|
||||||
units: [],
|
units: [],
|
||||||
mealPlans: [],
|
mealPlans: [],
|
||||||
icon: {
|
icon: {
|
||||||
alert: '\ue900',
|
back: '\ue900',
|
||||||
back: '\ue901',
|
bag: '\ue901',
|
||||||
bag: '\ue902',
|
bagged: '\ue902',
|
||||||
bagged: '\ue903',
|
cal: '\ue903',
|
||||||
cal: '\ue904',
|
category: '\ue904',
|
||||||
cam: '\ue905',
|
check: '\ue905',
|
||||||
category: '\ue906',
|
cog: '\ue906',
|
||||||
cog: '\ue907',
|
cuisine: '\ue907',
|
||||||
comb: '\ue908',
|
db: '\ue908',
|
||||||
cuisine: '\ue909',
|
del: '\ue909',
|
||||||
del: '\ue90a',
|
diff: '\ue90a',
|
||||||
diff: '\ue90b',
|
don: '\ue90b',
|
||||||
don: '\ue90c',
|
done: '\ue90c',
|
||||||
done: '\ue90d',
|
edit: '\ue90d',
|
||||||
edit: '\ue90e',
|
exp: '\ue90e',
|
||||||
exp: '\ue910',
|
fav: '\ue90f',
|
||||||
fav: '\ue911',
|
faved: '\ue910',
|
||||||
faved: '\ue912',
|
filter: '\ue911',
|
||||||
folder: '\ue913',
|
folder: '\ue912',
|
||||||
gh: '\ue914',
|
gh: '\ue913',
|
||||||
help: '\ue915',
|
help: '\ue914',
|
||||||
home: '\ue916',
|
home: '\ue915',
|
||||||
img: '\ue917',
|
img: '\ue916',
|
||||||
imp: '\ue918',
|
imp: '\ue917',
|
||||||
info: '\ue919',
|
info: '\ue918',
|
||||||
items: '\ue91a',
|
interface: '\ue919',
|
||||||
l1: '\ue91b',
|
lang: '\ue91a',
|
||||||
l2: '\ue91c',
|
layout: '\ue91b',
|
||||||
l3: '\ue91d',
|
left: '\ue91c',
|
||||||
lang: '\ue91e',
|
menu: '\ue91d',
|
||||||
left: '\ue91f',
|
opts: '\ue91e',
|
||||||
menu: '\ue920',
|
plus: '\ue91f',
|
||||||
noresult: '\ue921',
|
price: '\ue920',
|
||||||
notes: '\ue922',
|
priv: '\ue921',
|
||||||
plus: '\ue923',
|
reset: '\ue922',
|
||||||
plusc: '\ue924',
|
right: '\ue923',
|
||||||
price: '\ue925',
|
save: '\ue924',
|
||||||
priv: '\ue926',
|
sear: '\ue925',
|
||||||
err: '\ue90f',
|
share: '\ue926',
|
||||||
res: '\ue927',
|
shuf: '\ue927',
|
||||||
reset: '\ue928',
|
sort: '\ue928',
|
||||||
right: '\ue929',
|
star: '\ue929',
|
||||||
save: '\ue92a',
|
starred: '\ue92a',
|
||||||
sear: '\ue92b',
|
tag: '\ue92b',
|
||||||
selall: '\ue92c',
|
tg: '\ue92c',
|
||||||
share: '\ue92d',
|
theme: '\ue92d',
|
||||||
shuf: '\ue92e',
|
time: '\ue92e',
|
||||||
sort: '\ue92f',
|
tod: '\ue92f',
|
||||||
star: '\ue930',
|
trans: '\ue930',
|
||||||
starred: '\ue931',
|
tried: '\ue931',
|
||||||
steps: '\ue932',
|
try: '\ue932',
|
||||||
succ: '\ue933',
|
uncheck: '\ue933',
|
||||||
tag: '\ue934',
|
undo: '\ue934',
|
||||||
text: '\ue935',
|
week: '\ue935',
|
||||||
tg: '\ue936',
|
x: '\ue936',
|
||||||
theme: '\ue937',
|
|
||||||
time: '\ue938',
|
|
||||||
timer: '\ue939',
|
|
||||||
tod: '\ue93a',
|
|
||||||
trans: '\ue93b',
|
|
||||||
tried: '\ue93c',
|
|
||||||
try: '\ue93d',
|
|
||||||
unit: '\ue93e',
|
|
||||||
x: '\ue93f',
|
|
||||||
yield: '\ue940',
|
|
||||||
zip: '\ue941',
|
|
||||||
},
|
},
|
||||||
currentComponent: 'EnRecipes',
|
currentComponent: 'EnRecipes',
|
||||||
sortType: 'Oldest first',
|
sortType: 'Oldest first',
|
||||||
|
@ -298,11 +287,19 @@ export default new Vuex.Store({
|
||||||
selectedCategory: null,
|
selectedCategory: null,
|
||||||
selectedTag: null,
|
selectedTag: null,
|
||||||
appTheme: 'Light',
|
appTheme: 'Light',
|
||||||
|
mondayFirst: false,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
clearImportSummary(state) {
|
||||||
|
for (const key in state.importSummary) state.importSummary[key] = 0
|
||||||
|
},
|
||||||
|
setFirstDay(state, bool) {
|
||||||
|
state.mondayFirst = bool
|
||||||
|
ApplicationSettings.setBoolean('mondayFirst', bool)
|
||||||
|
},
|
||||||
setTheme(state, theme) {
|
setTheme(state, theme) {
|
||||||
ApplicationSettings.setString('appTheme', theme)
|
|
||||||
state.appTheme = theme
|
state.appTheme = theme
|
||||||
|
ApplicationSettings.setString('appTheme', theme)
|
||||||
},
|
},
|
||||||
clearFilter(state) {
|
clearFilter(state) {
|
||||||
state.selectedCuisine = state.selectedCategory = state.selectedTag = null
|
state.selectedCuisine = state.selectedCategory = state.selectedTag = null
|
||||||
|
@ -671,6 +668,7 @@ export default new Vuex.Store({
|
||||||
},
|
},
|
||||||
setShake(state, shake) {
|
setShake(state, shake) {
|
||||||
state.shakeEnabled = shake
|
state.shakeEnabled = shake
|
||||||
|
ApplicationSettings.setBoolean('shakeEnabled', shake)
|
||||||
},
|
},
|
||||||
setRating(state, { id, recipe, rating }) {
|
setRating(state, { id, recipe, rating }) {
|
||||||
let index = state.recipes.indexOf(
|
let index = state.recipes.indexOf(
|
||||||
|
@ -706,6 +704,12 @@ export default new Vuex.Store({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
clearImportSummary({ commit }) {
|
||||||
|
commit('clearImportSummary')
|
||||||
|
},
|
||||||
|
setFirstDay({ commit }, bool) {
|
||||||
|
commit('setFirstDay', bool)
|
||||||
|
},
|
||||||
setTheme({ commit }, theme) {
|
setTheme({ commit }, theme) {
|
||||||
commit('setTheme', theme)
|
commit('setTheme', theme)
|
||||||
},
|
},
|
||||||
|
@ -715,7 +719,7 @@ export default new Vuex.Store({
|
||||||
setLayout({ commit }, type) {
|
setLayout({ commit }, type) {
|
||||||
commit('setLayout', type)
|
commit('setLayout', type)
|
||||||
},
|
},
|
||||||
setSortTypeAction({ commit }, sortType) {
|
setSortType({ commit }, sortType) {
|
||||||
commit('setSortType', sortType)
|
commit('setSortType', sortType)
|
||||||
},
|
},
|
||||||
initRecipes({ commit }) {
|
initRecipes({ commit }) {
|
||||||
|
@ -778,7 +782,7 @@ export default new Vuex.Store({
|
||||||
unSyncCombinationsAction({ commit }, combinations) {
|
unSyncCombinationsAction({ commit }, combinations) {
|
||||||
commit('unSyncCombinations', combinations)
|
commit('unSyncCombinations', combinations)
|
||||||
},
|
},
|
||||||
setShakeAction({ commit }, shake) {
|
setShake({ commit }, shake) {
|
||||||
commit('setShake', shake)
|
commit('setShake', shake)
|
||||||
},
|
},
|
||||||
setRatingAction({ commit }, rating) {
|
setRatingAction({ commit }, rating) {
|
||||||
|
|
|
@ -5,4 +5,5 @@
|
||||||
- Improvement: Faster app start and navigation
|
- Improvement: Faster app start and navigation
|
||||||
- Added app languages: French, Italian, Norwegian Bokmål
|
- Added app languages: French, Italian, Norwegian Bokmål
|
||||||
- Updated translations
|
- Updated translations
|
||||||
|
- Dropped support for Android Lollipop
|
||||||
- Several bug fixes
|
- Several bug fixes
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": "./",
|
"baseUrl": "./",
|
||||||
"paths": {
|
"paths": {
|
||||||
"~/*": ["app/*"]
|
"~/*": ["app/*"],
|
||||||
|
"@/*": ["app/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["app/**/*"]
|
"include": ["app/**/*"]
|
||||||
|
|
|
@ -7,5 +7,6 @@ export default {
|
||||||
android: {
|
android: {
|
||||||
v8Flags: "--expose_gc",
|
v8Flags: "--expose_gc",
|
||||||
markingMode: "none",
|
markingMode: "none",
|
||||||
}
|
},
|
||||||
|
// profiling: 'timeline'
|
||||||
} as NativeScriptConfig
|
} as NativeScriptConfig
|
||||||
|
|
18541
package-lock.json
generated
18541
package-lock.json
generated
File diff suppressed because it is too large
Load diff
21
package.json
21
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "enrecipes",
|
"name": "enrecipes",
|
||||||
"main": "main.js",
|
"main": "./app/main.js",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "A native application built with NativeScript-Vue",
|
"description": "A native application built with NativeScript-Vue",
|
||||||
"homepage": "https://enrecipes.vercel.app/",
|
"homepage": "https://enrecipes.vercel.app/",
|
||||||
|
@ -18,27 +18,22 @@
|
||||||
},
|
},
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nativescript-community/perms": "^2.1.5",
|
|
||||||
"@nativescript-community/ui-collectionview": "^4.0.29",
|
"@nativescript-community/ui-collectionview": "^4.0.29",
|
||||||
"@nativescript/core": "7.3.0",
|
"@nativescript/core": "~8.0.1",
|
||||||
"@nativescript/localize": "^5.0.4",
|
"@nativescript/localize": "^5.0.4",
|
||||||
"@nativescript/social-share": "^2.0.4",
|
"@nativescript/social-share": "^2.0.4",
|
||||||
"@nativescript/zip": "^5.0.0",
|
|
||||||
"@triniwiz/nativescript-accelerometer": "^4.0.3",
|
"@triniwiz/nativescript-accelerometer": "^4.0.3",
|
||||||
"@triniwiz/nativescript-couchbase": "^1.2.2",
|
"@triniwiz/nativescript-couchbase": "^1.2.2",
|
||||||
|
"@triniwiz/nativescript-image-zoom": "^4.0.0",
|
||||||
"nativescript-imagecropper": "^4.0.1",
|
"nativescript-imagecropper": "^4.0.1",
|
||||||
"nativescript-intl": "^4.0.2",
|
"nativescript-intl": "^4.0.2",
|
||||||
"nativescript-vue": "^2.8.4",
|
"nativescript-vue": "~2.9.0",
|
||||||
"vuex": "^3.6.2"
|
"vuex": "^3.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.10",
|
"@nativescript/android": "8.0.0",
|
||||||
"@babel/preset-env": "^7.12.11",
|
"@nativescript/webpack": "beta",
|
||||||
"@nativescript/android": "7.0.1",
|
"nativescript-vue-template-compiler": "~2.9.0",
|
||||||
"@nativescript/webpack": "4.1.0",
|
"sass": "^1.32.8"
|
||||||
"babel-loader": "^8.2.2",
|
|
||||||
"nativescript-vue-template-compiler": "^2.8.3",
|
|
||||||
"node-sass": "^4.14.1",
|
|
||||||
"vue-loader": "^15.9.6"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue