Get Recipes Route Rewrite (#339)

* deprecate old route

* auto-gen

* recipe card infinite scroll

* fix datatable

* set hard-limit option

* add loader

* set scroll on navigation

* add auto-import

* fix slow initial load

* remove console.logs

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-04-22 22:13:55 -08:00 committed by GitHub
parent 80f8806604
commit 8e4b951ecc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 128 additions and 73 deletions

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,6 @@ import { baseURL } from "./api-utils";
import { apiReq } from "./api-utils";
import { store } from "../store";
import { router } from "../main";
import qs from "qs";
const prefix = baseURL + "recipes/";
@ -78,22 +77,10 @@ export const recipeAPI = {
router.push(`/`);
},
async allByKeys(recipeKeys, num = 9999) {
const response = await apiReq.get(recipeURLs.allRecipes, {
params: {
keys: recipeKeys,
num: num,
},
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: "repeat" });
},
async allSummary(start = 0, limit = 9999) {
const response = await apiReq.get(recipeURLs.summary, {
params: { start: start, limit: limit },
});
return response.data;
},
async allSummary() {
const response = await apiReq.get(recipeURLs.summary);
return response.data;
},

View File

@ -45,7 +45,7 @@
</template>
<script>
import DataTable from "@/components/ImportSummaryDialog";
import DataTable from "@/components/ImportSummaryDialog/DataTable";
export default {
components: {
DataTable,

View File

@ -116,6 +116,7 @@ export default {
},
async mounted() {
await this.$store.dispatch("requestCurrentGroup");
await this.$store.dispatch("requestAllRecipes");
await this.buildMealStore();
},
@ -151,6 +152,9 @@ export default {
const recipes = this.items.filter(x => !this.usedRecipes.includes(x));
return recipes.length > 0 ? recipes : this.items;
},
allRecipes() {
return this.$store.getters.getRecentRecipes;
},
},
methods: {
@ -159,15 +163,7 @@ export default {
this.items = await api.recipes.getAllByCategory(categories);
if (this.items.length === 0) {
const keys = [
"name",
"slug",
"image",
"description",
"dateAdded",
"rating",
];
this.items = await api.recipes.allByKeys(keys);
this.items = this.allRecipes;
}
},
getRandom(list) {

View File

@ -9,7 +9,7 @@
>
<ConfirmationDialog
:title="$t('recipe.delete-recipe')"
:message="$t('recipe.delete-ConfirmationDialog')"
:message="$t('recipe.delete-confirmation')"
color="error"
icon="mdi-alert-circle"
ref="deleteRecipieConfirm"

View File

@ -33,11 +33,6 @@ export default {
totalTime: String,
performTime: String,
},
watch: {
showCards(val) {
console.log(val);
},
},
computed: {
showCards() {
return [this.prepTime, this.totalTime, this.performTime].some(

View File

@ -78,6 +78,16 @@
</v-col>
</v-row>
</div>
<div v-intersect="bumpList" class="d-flex">
<v-progress-circular
v-if="loading"
class="mx-auto mt-1"
:size="50"
:width="7"
color="primary"
indeterminate
></v-progress-circular>
</div>
</div>
</template>
@ -96,10 +106,16 @@ export default {
title: {
default: null,
},
recipes: Array,
cardLimit: {
default: 999,
hardLimit: {
default: 99999,
},
recipes: Array,
},
data() {
return {
cardLimit: 30,
loading: false,
};
},
computed: {
viewScale() {
@ -113,6 +129,22 @@ export default {
}
},
},
methods: {
bumpList() {
const newCardLimit = Math.min(this.cardLimit + 20, this.hardLimit);
if (this.loading === false && newCardLimit > this.cardLimit) {
this.setLoader();
}
this.cardLimit = newCardLimit;
},
async setLoader() {
this.loading = true;
await new Promise(r => setTimeout(r, 3000));
this.loading = false;
},
},
};
</script>

View File

@ -89,7 +89,6 @@ export default {
searchSlug: "",
search: "",
menuModel: false,
data: [],
result: [],
fuseResults: [],
isDark: false,
@ -107,9 +106,12 @@ export default {
},
mounted() {
this.isDark = this.$store.getters.getIsDark;
this.data = this.$store.getters.getRecentRecipes;
this.$store.dispatch("requestAllRecipes");
},
computed: {
data() {
return this.$store.getters.getRecentRecipes;
},
autoResults() {
return this.fuseResults.length > 1 ? this.fuseResults : this.results;
},

View File

@ -135,7 +135,6 @@ export default {
this.groupSettings.webhookUrls.splice(index, 1);
},
async saveGroupSettings() {
console.log(this.groupSettings);
await api.groups.update(this.groupSettings);
await this.$store.dispatch("requestCurrentGroup");
this.getSiteSettings();

View File

@ -42,7 +42,7 @@
</template>
<script>
import DataTable from "@/components/ImportSummaryDialog";
import DataTable from "@/components/ImportSummaryDialog/DataTable";
export default {
components: {
DataTable,

View File

@ -223,8 +223,7 @@ export default {
this.settings.categories.splice(index, 1);
},
async saveSettings() {
const newSettings = await api.siteSettings.update(this.settings);
console.log("New Settings", newSettings);
await api.siteSettings.update(this.settings);
this.getOptions();
},
},

View File

@ -5,7 +5,7 @@
v-if="siteSettings.showRecent"
:title="$t('page.recent')"
:recipes="recentRecipes"
:card-limit="siteSettings.cardsPerSection"
:hard-limit="siteSettings.cardsPerSection"
/>
<CardSection
:sortable="true"
@ -13,7 +13,7 @@
:key="section.name + section.position"
:title="section.name"
:recipes="section.recipes"
:card-limit="siteSettings.cardsPerSection"
:hard-limit="siteSettings.cardsPerSection"
@sort="sortAZ(index)"
@sort-recent="sortRecent(index)"
/>

View File

@ -5,7 +5,6 @@
:sortable="true"
:title="$t('page.all-recipes')"
:recipes="allRecipes"
:card-limit="9999"
@sort="sortAZ"
@sort-recent="sortRecent"
/>
@ -23,6 +22,9 @@ export default {
data() {
return {};
},
mounted() {
this.$store.dispatch("requestAllRecipes");
},
computed: {
allRecipes() {
return this.$store.getters.getRecentRecipes;

View File

@ -26,7 +26,9 @@
<v-row dense class="mt-0 flex-row align-center justify-space-around">
<v-col>
<h3 class="pl-2 text-center headline">{{$t('search.category-filter')}}</h3>
<h3 class="pl-2 text-center headline">
{{ $t("search.category-filter") }}
</h3>
<FilterSelector class="mb-1" @update="updateCatParams" />
<CategoryTagSelector
:solo="true"
@ -36,7 +38,9 @@
/>
</v-col>
<v-col>
<h3 class="pl-2 text-center headline">{{$t('search.tag-filter')}}</h3>
<h3 class="pl-2 text-center headline">
{{ $t("search.tag-filter") }}
</h3>
<FilterSelector class="mb-1" @update="updateTagParams" />
<CategoryTagSelector
@ -113,6 +117,9 @@ export default {
},
};
},
mounted() {
this.$store.dispatch("requestAllRecipes");
},
computed: {
allRecipes() {
return this.$store.getters.getRecentRecipes;

View File

@ -24,6 +24,9 @@ export const routes = [
const router = new VueRouter({
routes,
mode: process.env.NODE_ENV === "production" ? "history" : "hash",
scrollBehavior() {
return { x: 0, y: 0 };
},
});
const DEFAULT_TITLE = "Mealie";

View File

@ -53,19 +53,18 @@ const store = new Vuex.Store({
},
actions: {
async requestRecentRecipes() {
// const keys = [
// "name",
// "slug",
// "image",
// "description",
// "dateAdded",
// "rating",
// ];
const payload = await api.recipes.allSummary();
async requestRecentRecipes({ getters }) {
const payload = await api.recipes.allSummary(0, 30);
const recent = getters.getRecentRecipes;
if (recent.length >= 30) return;
this.commit("setRecentRecipes", payload);
},
async requestAllRecipes({ getters }) {
const recent = getters.getRecentRecipes;
const start = recent.length + 1;
const payload = await api.recipes.allSummary(start, 9999);
this.commit("setRecentRecipes", [...recent, ...payload]);
},
async requestCategories({ commit }) {
const categories = await api.categories.getAll();
commit("setAllCategories", categories);
@ -74,7 +73,6 @@ const store = new Vuex.Store({
const tags = await api.tags.getAll();
commit("setAllTags", tags);
},
async requestAppInfo({ commit }) {
const response = await api.meta.getAppInfo();
commit("setAppInfo", response);

View File

@ -16,10 +16,12 @@ class BaseDocument:
self.schema: BaseModel
# TODO: Improve Get All Query Functionality
def get_all(self, session: Session, limit: int = None, order_by: str = None, override_schema=None) -> List[dict]:
def get_all(
self, session: Session, limit: int = None, order_by: str = None, start=0, end=9999, override_schema=None
) -> List[dict]:
eff_schema = override_schema or self.schema
return [eff_schema.from_orm(x) for x in session.query(self.sql_model).limit(limit).all()]
return [eff_schema.from_orm(x) for x in session.query(self.sql_model).offset(start).limit(limit).all()]
def get_all_limit_columns(self, session: Session, fields: List[str], limit: int = None) -> List[SqlAlchemyBase]:
"""Queries the database for the selected model. Restricts return responses to the

View File

@ -12,16 +12,26 @@ router = APIRouter(tags=["Query All Recipes"])
@router.get("/api/recipes/summary")
async def get_recipe_summary(
skip=0,
end=9999,
start=0,
limit=9999,
session: Session = Depends(generate_session),
):
""" Returns the summary data for recipes in the database """
"""
Returns key the recipe summary data for recipes in the database. You can perform
slice operations to set the skip/end amounts for recipes. All recipes are sorted by the added date.
return db.recipes.get_all(session, limit=end, override_schema=RecipeSummary)
**Query Parameters**
- skip: The database entry to start at. (0 Indexed)
- end: The number of entries to return.
skip=2, end=10 will return entries
"""
return db.recipes.get_all(session, limit=limit, start=start, override_schema=RecipeSummary)
@router.get("/api/recipes")
@router.get("/api/recipes", deprecated=True)
def get_all_recipes(
keys: Optional[List[str]] = Query(...),
num: Optional[int] = 100,
@ -54,7 +64,7 @@ def get_all_recipes(
return db.recipes.get_all_limit_columns(session, keys, limit=num)
@router.post("/api/recipes")
@router.post("/api/recipes", deprecated=True)
def get_all_recipes_post(body: AllRecipeRequest, session: Session = Depends(generate_session)):
"""
Returns key data for all recipes based off the body data provided.

View File

@ -1,11 +1,34 @@
# Make .env in this folder if needed.
# The Default Group Assigned to All Users
DEFAULT_GROUP=Home
PRODUCTION=False
API_PORT=9000
API_DOCS=True
DB_TYPE=sqlite
# The Default Credentials for the Super User
DEFAULT_EMAIL=changeme@email.com
DEFAULT_PASSWORD=MyPassword
TOKEN_TIME=2
# Determines Production Mode, This will set the directory path to use for data storage
PRODUCTION=False
# API Port for Pythong Server
API_PORT=9000
# Exposes /docs and /redoc on the server
API_DOCS=True
# Sets the Database type to use. Currently the only supported options is 'sqlite'
DB_TYPE=sqlite
# Sets the token expiration time in hours.
TOKEN_TIME=24
# NOT USED
SFTP_USERNAME=None
SFTP_PASSWORD=None
# NOT USED Auto Import Options
AUTO_IMPORT=True
AUTO_IMPORT_RECIPES=True
AUTO_IMPORT_SETTINGS=True
AUTO_IMPORT_PAGES=True
AUTO_IMPORT_THEMES=True
AUTO_IMPORT_USERS=True
AUTO_IMPORT_GROUPS=True