Frontend Fixes + Adjust Caddyfile (#518)

* token error handling

* Add additional settings to recipes

* fixes #515

* remove index.html

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-06-14 19:37:38 -08:00 committed by GitHub
parent 17a7d0b31e
commit d7c883feca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 48 additions and 319 deletions

View File

@ -29,7 +29,7 @@
handle { handle {
header @static Cache-Control max-age=31536000 header @static Cache-Control max-age=31536000
root * /app/dist root * /app/dist
try_files {path}.html {path} /index.html try_files {path}.html {path} /
file_server file_server
} }
} }

View File

@ -1,42 +0,0 @@
import { recipeIngredient } from "./recipeIngredient";
import { recipeNumber } from "./recipeNumber";
export const ingredientScaler = {
process(ingredientArray, scale) {
console.log(scale);
let workingArray = ingredientArray.map(x =>
ingredientScaler.markIngredient(x)
);
return workingArray.map(x => ingredientScaler.adjustIngredients(x, scale));
},
adjustIngredients(ingredient, scale) {
var scaledQuantity = new recipeNumber(ingredient.quantity).multiply(scale);
const newText = ingredient.text.replace(
ingredient.quantity,
scaledQuantity
);
return { ...ingredient, quantity: scaledQuantity, text: newText };
},
markIngredient(ingredient) {
console.log(ingredient);
const returnVar = ingredient.replace(
/^([\d/?[^\s&]*)(?:&nbsp;|\s)(\w*)/g,
(match, quantity, unit) => {
return `${unit}${quantity},${match}`;
}
);
const split = returnVar.split(",");
const [unit, quantity, match] = split;
console.log("Split", unit, quantity, match);
const n = new recipeNumber(quantity);
const i = new recipeIngredient(n, unit);
const serializedQuantity = n.isFraction() ? n.toImproperFraction() : n;
return {
unit: i,
quantity: serializedQuantity.toString(),
text: match,
};
},
};

View File

@ -1,75 +0,0 @@
export const recipeIngredient = function(quantity, unit) {
this.quantity = quantity;
this.unit = unit;
};
recipeIngredient.prototype.isSingular = function() {
return this.quantity > 0 && this.quantity <= 1;
};
recipeIngredient.prototype.pluralize = function() {
if (this.isSingular()) {
return this.unit;
} else {
return `${this.unit}s`;
}
};
recipeIngredient.prototype.getSingularUnit = function() {
if (this.isSingular()) {
return this.unit;
} else {
return this.unit.replace(/s$/, "");
}
};
recipeIngredient.prototype.toString = function() {
return `${this.quantity.toString()} ${this.pluralize()}`;
};
recipeIngredient.prototype.convertUnits = function() {
const conversion = recipeIngredient.CONVERSIONS[this.unit] || {};
if (conversion.min && this.quantity < conversion.min.value) {
this.unit = conversion.min.next;
this.quantity.multiply(conversion.to[this.unit]);
} else if (conversion.max && this.quantity >= conversion.max.value) {
this.unit = conversion.max.next;
this.quantity.multiply(conversion.to[this.unit]);
}
return this;
};
recipeIngredient.CONVERSIONS = {
cup: {
to: {
tablespoon: 16,
},
min: {
value: 1 / 4,
next: "tablespoon",
},
},
tablespoon: {
to: {
teaspoon: 3,
cup: 1 / 16,
},
min: {
value: 1,
next: "teaspoon",
},
max: {
value: 4,
next: "cup",
},
},
teaspoon: {
to: {
tablespoon: 1 / 3,
},
max: {
value: 3,
next: "tablespoon",
},
},
};

View File

@ -1,166 +0,0 @@
export const recipeNumber = function(number) {
const match = number.match(
/^(?:(\d+)|(?:(\d+)(?: |&nbsp;))?(?:(\d+)\/(\d+))?)$/
);
if (!match || !match[0] || match[4] == "0") {
throw `Invalid number: "${number}".`;
}
this.wholeNumber = +(match[1] || match[2]);
this.numerator = +match[3];
this.denominator = +match[4];
};
/**
* Determines if the number is a fraction.
* @this {recipeNumber}
* @return {boolean} If the number is a fraction.
*/
recipeNumber.prototype.isFraction = function() {
return !!(this.numerator && this.denominator);
};
/**
* Determines if the fraction is proper, which is defined as
* the numerator being strictly less than the denominator.
* @this {recipeNumber}
* @return {boolean} If the fraction is proper.
*/
recipeNumber.prototype.isProperFraction = function() {
return this.numerator < this.denominator;
};
/**
* Determines if the fraction is improper, which is defined as
* the numerator being greater than or equal to the denominator.
* @this {recipeNumber}
* @return {boolean} If the fraction is improper.
*/
recipeNumber.prototype.isImproperFraction = function() {
return this.numerator >= this.denominator;
};
/**
* Determines if the fraction is mixed, which is defined as
* a whole number with a proper fraction.
* @this {recipeNumber}
* @return {boolean} If the fraction is mixed.
*/
recipeNumber.prototype.isMixedFraction = function() {
return this.isProperFraction() && !isNaN(this.wholeNumber);
};
/**
* Simplifies fractions. Examples:
* 3/2 = 1 1/2
* 4/2 = 2
* 1 3/2 = 2 1/2
* 0/1 = 0
* 1 0/1 = 1
* @this {recipeNumber}
* @return {recipeNumber} The instance.
*/
recipeNumber.prototype.simplifyFraction = function() {
if (this.isImproperFraction()) {
this.wholeNumber |= 0;
this.wholeNumber += Math.floor(this.numerator / this.denominator);
const modulus = this.numerator % this.denominator;
if (modulus) {
this.numerator = modulus;
} else {
this.numerator = this.denominator = NaN;
}
} else if (this.numerator == 0) {
this.wholeNumber |= 0;
this.numerator = this.denominator = NaN;
}
return this;
};
/**
* Reduces a fraction. Examples:
* 2/6 = 1/3
* 6/2 = 3/1
* @this {recipeNumber}
* @return {recipeNumber} The instance.
*/
recipeNumber.prototype.reduceFraction = function() {
if (this.isFraction()) {
const gcd = recipeNumber.gcd(this.numerator, this.denominator);
this.numerator /= gcd;
this.denominator /= gcd;
}
return this;
};
/**
* Converts proper fractions to improper fractions. Examples:
* 1 1/2 = 3/2
* 3/2 = 3/2
* 1/2 = 1/2
* 2 = 2
*
* @this {recipeNumber}
* @return {recipeNumber} The instance.
*/
recipeNumber.prototype.toImproperFraction = function() {
if (!isNaN(this.wholeNumber)) {
this.numerator |= 0;
this.denominator = this.denominator || 1;
this.numerator += this.wholeNumber * this.denominator;
this.wholeNumber = NaN;
}
return this;
};
/**
* Multiplies the number by some decimal value.
* @param {number} multiplier The multiplier.
* @this {recipeNumber}
* @return {recipeNumber} The instance.
*/
recipeNumber.prototype.multiply = function(multiplier) {
this.toImproperFraction();
this.numerator *= multiplier;
return this.reduceFraction().simplifyFraction();
};
/**
* Gets a string representation of the number.
* @this {recipeNumber}
* @return {string} The string representation of the number.
*/
recipeNumber.prototype.toString = function() {
let number = "";
let fraction = "";
if (!isNaN(this.wholeNumber)) {
number += this.wholeNumber;
}
if (this.isFraction()) {
fraction = `${this.numerator}/${this.denominator}`;
}
if (number && fraction) {
number += ` ${fraction}`;
}
return number || fraction;
};
/**
* Gets a numeric representation of the number.
* @this {recipeNumber}
* @return {number} The numeric representation of the number.
*/
recipeNumber.prototype.valueOf = function() {
let value = this.wholeNumber || 0;
value += this.numerator / this.denominator || 0;
return value;
};
/**
* Euclid's algorithm to find the greatest common divisor of two numbers.
* @param {number} a One number.
* @param {number} b Another number.
* @return {number} The GCD of the numbers.
*/
recipeNumber.gcd = function gcd(a, b) {
return b ? recipeNumber.gcd(b, a % b) : a;
};

View File

@ -36,7 +36,6 @@ export const recipeAPI = {
async requestDetails(recipeSlug) { async requestDetails(recipeSlug) {
const response = await apiReq.getSafe(API_ROUTES.recipesRecipeSlug(recipeSlug)); const response = await apiReq.getSafe(API_ROUTES.recipesRecipeSlug(recipeSlug));
console.log(response);
return response; return response;
}, },

View File

@ -1,20 +1,16 @@
import { API_ROUTES } from "./apiRoutes"; import { API_ROUTES } from "./apiRoutes";
import { apiReq } from "./api-utils"; import { apiReq } from "./api-utils";
import axios from "axios";
import i18n from "@/i18n.js"; import i18n from "@/i18n.js";
export const userAPI = { export const userAPI = {
async login(formData) { async login(formData) {
let response = await apiReq.post(API_ROUTES.authToken, formData, null, function() { let response = await apiReq.post(API_ROUTES.authToken, formData, null, () => {
return i18n.t("user.user-successfully-logged-in"); return i18n.t("user.user-successfully-logged-in");
}); });
return response; return response;
}, },
async refresh() { async refresh() {
let response = await axios.get(API_ROUTES.authRefresh).catch(function(event) { return apiReq.getSafe(API_ROUTES.authRefresh);
console.log("Fetch failed", event);
});
return response.data ? response.data : false;
}, },
async allUsers() { async allUsers() {
let response = await apiReq.get(API_ROUTES.users); let response = await apiReq.get(API_ROUTES.users);
@ -29,8 +25,7 @@ export const userAPI = {
); );
}, },
async self() { async self() {
let response = await apiReq.get(API_ROUTES.usersSelf); return apiReq.getSafe(API_ROUTES.usersSelf);
return response.data;
}, },
async byID(id) { async byID(id) {
let response = await apiReq.get(API_ROUTES.usersId(id)); let response = await apiReq.get(API_ROUTES.usersId(id));

View File

@ -106,9 +106,6 @@ export default {
}, },
watch: { watch: {
startDate(val) {
console.log(val);
},
dateDif() { dateDif() {
this.planDays = []; this.planDays = [];
for (let i = 0; i < this.dateDif; i++) { for (let i = 0; i < this.dateDif; i++) {

View File

@ -10,12 +10,10 @@
<slot> </slot> <slot> </slot>
</v-img> </v-img>
<div class="icon-slot" v-else @click="$emit('click')"> <div class="icon-slot" v-else @click="$emit('click')">
<div>
<slot> </slot>
</div>
<v-icon color="primary" class="icon-position" :size="iconSize"> <v-icon color="primary" class="icon-position" :size="iconSize">
{{ $globals.icons.primary }} {{ $globals.icons.primary }}
</v-icon> </v-icon>
<slot> </slot>
</div> </div>
</template> </template>

View File

@ -82,9 +82,6 @@ export default {
this.$set(this.editKeys, comment.id, false); this.$set(this.editKeys, comment.id, false);
} }
}, },
editKeys() {
console.log(this.editKeys);
},
}, },
methods: { methods: {
resetImage() { resetImage() {
@ -103,7 +100,6 @@ export default {
this.$emit(UPDATE_COMMENT_EVENT); this.$emit(UPDATE_COMMENT_EVENT);
}, },
async createNewComment() { async createNewComment() {
console.log(this.slug);
await api.recipes.createComment(this.slug, { text: this.newComment }); await api.recipes.createComment(this.slug, { text: this.newComment });
this.$emit(NEW_COMMENT_EVENT); this.$emit(NEW_COMMENT_EVENT);

View File

@ -47,6 +47,8 @@ export default {
showNutrition: this.$t("recipe.show-nutrition-values"), showNutrition: this.$t("recipe.show-nutrition-values"),
showAssets: this.$t("recipe.show-assets"), showAssets: this.$t("recipe.show-assets"),
landscapeView: this.$t("recipe.landscape-view-coming-soon"), landscapeView: this.$t("recipe.landscape-view-coming-soon"),
disableComments: this.$t("recipe.disable-comments"),
disableAmount: this.$t("recipe.disable-amount"),
}; };
}, },
}, },

View File

@ -27,7 +27,7 @@
<Rating :value="rating" :name="name" :slug="slug" :small="true" /> <Rating :value="rating" :name="name" :slug="slug" :small="true" />
<v-spacer></v-spacer> <v-spacer></v-spacer>
<RecipeChips :truncate="true" :items="tags" :title="false" :limit="2" :small="true" :isCategory="false" /> <RecipeChips :truncate="true" :items="tags" :title="false" :limit="2" :small="true" :isCategory="false" />
<ContextMenu :slug="slug" :name="name"/> <ContextMenu :slug="slug" :name="name" />
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-hover> </v-hover>

View File

@ -210,6 +210,8 @@
"calories-suffix": "calories", "calories-suffix": "calories",
"carbohydrate-content": "Carbohydrate", "carbohydrate-content": "Carbohydrate",
"categories": "Categories", "categories": "Categories",
"disable-comments": "Disable Comments",
"disable-amount": "Disable Ingredient Amounts",
"delete-confirmation": "Are you sure you want to delete this recipe?", "delete-confirmation": "Are you sure you want to delete this recipe?",
"delete-recipe": "Delete Recipe", "delete-recipe": "Delete Recipe",
"description": "Description", "description": "Description",

View File

@ -150,7 +150,13 @@ export default {
methods: { methods: {
async refreshProfile() { async refreshProfile() {
this.user = await api.users.self(); const [response, err] = await api.users.self();
if (err) {
return; // TODO: Log or Notifty User of Error
}
this.user = response.data;
}, },
openAvatarPicker() { openAvatarPicker() {
this.showAvatarPicker = true; this.showAvatarPicker = true;

View File

@ -55,6 +55,7 @@
/> />
</v-card> </v-card>
<CommentsSection <CommentsSection
v-if="recipeDetails.settings && !recipeDetails.settings.disableComments"
class="mt-2 d-print-none" class="mt-2 d-print-none"
:slug="recipeDetails.slug" :slug="recipeDetails.slug"
:comments="recipeDetails.comments" :comments="recipeDetails.comments"

View File

@ -199,7 +199,6 @@ export default {
}, },
async importIngredients(selected) { async importIngredients(selected) {
const [response, error] = await api.recipes.requestDetails(selected.slug); const [response, error] = await api.recipes.requestDetails(selected.slug);
console.log(response);
if (error) { if (error) {
console.log(error); console.log(error);

View File

@ -56,8 +56,13 @@ const mutations = {
const actions = { const actions = {
async requestUserData({ getters, commit }) { async requestUserData({ getters, commit }) {
if (getters.getIsLoggedIn) { if (getters.getIsLoggedIn) {
const userData = await api.users.self(); const [response, err] = await api.users.self();
commit("setUserData", userData);
if (err) {
return; // TODO: Log or Notifty User of Error
}
commit("setUserData", response.data);
} }
}, },
@ -76,13 +81,15 @@ const actions = {
console.log("Not Logged In"); console.log("Not Logged In");
return; return;
} }
try {
let authResponse = await api.users.refresh(); const [response, err] = await api.users.refresh();
commit("setToken", authResponse.access_token);
} catch { if (err) {
console.log("Failed Token Refresh, Logging Out..."); console.log("Failed Token Refresh, Logging Out...");
commit("setIsLoggedIn", false); commit("setIsLoggedIn", false);
} }
commit("setToken", response.data.access_token);
}, },
async initTheme({ dispatch, getters }) { async initTheme({ dispatch, getters }) {

View File

@ -1,6 +1,6 @@
from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
from requests import Session from requests import Session
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Table, orm from sqlalchemy import Column, ForeignKey, Integer, String, Table, orm
ingredients_to_units = Table( ingredients_to_units = Table(
"ingredients_to_units", "ingredients_to_units",
@ -82,14 +82,10 @@ class RecipeIngredient(SqlAlchemyBase):
quantity = Column(Integer) quantity = Column(Integer)
# Extras # Extras
disable_amount = Column(Boolean, default=False)
def __init__( def __init__(self, title: str, note: str, unit: dict, food: dict, quantity: int, session: Session, **_) -> None:
self, title: str, note: str, unit: dict, food: dict, quantity: int, disable_amount: bool, session: Session, **_
) -> None:
self.title = title self.title = title
self.note = note self.note = note
self.unit = IngredientUnit.get_ref_or_create(session, unit) self.unit = IngredientUnit.get_ref_or_create(session, unit)
self.food = IngredientFood.get_ref_or_create(session, food) self.food = IngredientFood.get_ref_or_create(session, food)
self.quantity = quantity self.quantity = quantity
self.disable_amount = disable_amount

View File

@ -10,9 +10,21 @@ class RecipeSettings(SqlAlchemyBase):
show_nutrition = sa.Column(sa.Boolean) show_nutrition = sa.Column(sa.Boolean)
show_assets = sa.Column(sa.Boolean) show_assets = sa.Column(sa.Boolean)
landscape_view = sa.Column(sa.Boolean) landscape_view = sa.Column(sa.Boolean)
disable_amount = sa.Column(sa.Boolean, default=False)
disable_comments = sa.Column(sa.Boolean, default=False)
def __init__(self, public=True, show_nutrition=True, show_assets=True, landscape_view=True) -> None: def __init__(
self,
public=True,
show_nutrition=True,
show_assets=True,
landscape_view=True,
disable_amount=True,
disable_comments=False,
) -> None:
self.public = public self.public = public
self.show_nutrition = show_nutrition self.show_nutrition = show_nutrition
self.show_assets = show_assets self.show_assets = show_assets
self.landscape_view = landscape_view self.landscape_view = landscape_view
self.disable_amount = disable_amount
self.disable_comments = disable_comments

View File

@ -16,6 +16,8 @@ class RecipeSettings(CamelModel):
show_nutrition: bool = True show_nutrition: bool = True
show_assets: bool = True show_assets: bool = True
landscape_view: bool = True landscape_view: bool = True
disable_comments: bool = False
disable_amount: bool = False
class Config: class Config:
orm_mode = True orm_mode = True