mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-09 03:04:54 -04:00
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:
parent
80f8806604
commit
8e4b951ecc
File diff suppressed because one or more lines are too long
@ -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;
|
||||
},
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataTable from "@/components/ImportSummaryDialog";
|
||||
import DataTable from "@/components/ImportSummaryDialog/DataTable";
|
||||
export default {
|
||||
components: {
|
||||
DataTable,
|
||||
|
@ -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) {
|
||||
|
@ -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"
|
||||
|
@ -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(
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
},
|
||||
|
@ -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();
|
||||
|
@ -42,7 +42,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataTable from "@/components/ImportSummaryDialog";
|
||||
import DataTable from "@/components/ImportSummaryDialog/DataTable";
|
||||
export default {
|
||||
components: {
|
||||
DataTable,
|
||||
|
@ -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();
|
||||
},
|
||||
},
|
||||
|
@ -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)"
|
||||
/>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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";
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
35
template.env
35
template.env
@ -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
|
Loading…
x
Reference in New Issue
Block a user