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 { apiReq } from "./api-utils";
|
||||||
import { store } from "../store";
|
import { store } from "../store";
|
||||||
import { router } from "../main";
|
import { router } from "../main";
|
||||||
import qs from "qs";
|
|
||||||
|
|
||||||
const prefix = baseURL + "recipes/";
|
const prefix = baseURL + "recipes/";
|
||||||
|
|
||||||
@ -78,22 +77,10 @@ export const recipeAPI = {
|
|||||||
router.push(`/`);
|
router.push(`/`);
|
||||||
},
|
},
|
||||||
|
|
||||||
async allByKeys(recipeKeys, num = 9999) {
|
async allSummary(start = 0, limit = 9999) {
|
||||||
const response = await apiReq.get(recipeURLs.allRecipes, {
|
const response = await apiReq.get(recipeURLs.summary, {
|
||||||
params: {
|
params: { start: start, limit: limit },
|
||||||
keys: recipeKeys,
|
|
||||||
num: num,
|
|
||||||
},
|
|
||||||
paramsSerializer: params => {
|
|
||||||
return qs.stringify(params, { arrayFormat: "repeat" });
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
|
|
||||||
async allSummary() {
|
|
||||||
const response = await apiReq.get(recipeURLs.summary);
|
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DataTable from "@/components/ImportSummaryDialog";
|
import DataTable from "@/components/ImportSummaryDialog/DataTable";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
DataTable,
|
DataTable,
|
||||||
|
@ -116,6 +116,7 @@ export default {
|
|||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
await this.$store.dispatch("requestCurrentGroup");
|
await this.$store.dispatch("requestCurrentGroup");
|
||||||
|
await this.$store.dispatch("requestAllRecipes");
|
||||||
await this.buildMealStore();
|
await this.buildMealStore();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -151,6 +152,9 @@ export default {
|
|||||||
const recipes = this.items.filter(x => !this.usedRecipes.includes(x));
|
const recipes = this.items.filter(x => !this.usedRecipes.includes(x));
|
||||||
return recipes.length > 0 ? recipes : this.items;
|
return recipes.length > 0 ? recipes : this.items;
|
||||||
},
|
},
|
||||||
|
allRecipes() {
|
||||||
|
return this.$store.getters.getRecentRecipes;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@ -159,15 +163,7 @@ export default {
|
|||||||
this.items = await api.recipes.getAllByCategory(categories);
|
this.items = await api.recipes.getAllByCategory(categories);
|
||||||
|
|
||||||
if (this.items.length === 0) {
|
if (this.items.length === 0) {
|
||||||
const keys = [
|
this.items = this.allRecipes;
|
||||||
"name",
|
|
||||||
"slug",
|
|
||||||
"image",
|
|
||||||
"description",
|
|
||||||
"dateAdded",
|
|
||||||
"rating",
|
|
||||||
];
|
|
||||||
this.items = await api.recipes.allByKeys(keys);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getRandom(list) {
|
getRandom(list) {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
>
|
>
|
||||||
<ConfirmationDialog
|
<ConfirmationDialog
|
||||||
:title="$t('recipe.delete-recipe')"
|
:title="$t('recipe.delete-recipe')"
|
||||||
:message="$t('recipe.delete-ConfirmationDialog')"
|
:message="$t('recipe.delete-confirmation')"
|
||||||
color="error"
|
color="error"
|
||||||
icon="mdi-alert-circle"
|
icon="mdi-alert-circle"
|
||||||
ref="deleteRecipieConfirm"
|
ref="deleteRecipieConfirm"
|
||||||
|
@ -33,11 +33,6 @@ export default {
|
|||||||
totalTime: String,
|
totalTime: String,
|
||||||
performTime: String,
|
performTime: String,
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
showCards(val) {
|
|
||||||
console.log(val);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
showCards() {
|
showCards() {
|
||||||
return [this.prepTime, this.totalTime, this.performTime].some(
|
return [this.prepTime, this.totalTime, this.performTime].some(
|
||||||
|
@ -78,6 +78,16 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -96,10 +106,16 @@ export default {
|
|||||||
title: {
|
title: {
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
recipes: Array,
|
hardLimit: {
|
||||||
cardLimit: {
|
default: 99999,
|
||||||
default: 999,
|
|
||||||
},
|
},
|
||||||
|
recipes: Array,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cardLimit: 30,
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
viewScale() {
|
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>
|
</script>
|
||||||
|
|
||||||
|
@ -89,7 +89,6 @@ export default {
|
|||||||
searchSlug: "",
|
searchSlug: "",
|
||||||
search: "",
|
search: "",
|
||||||
menuModel: false,
|
menuModel: false,
|
||||||
data: [],
|
|
||||||
result: [],
|
result: [],
|
||||||
fuseResults: [],
|
fuseResults: [],
|
||||||
isDark: false,
|
isDark: false,
|
||||||
@ -107,9 +106,12 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.isDark = this.$store.getters.getIsDark;
|
this.isDark = this.$store.getters.getIsDark;
|
||||||
this.data = this.$store.getters.getRecentRecipes;
|
this.$store.dispatch("requestAllRecipes");
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
data() {
|
||||||
|
return this.$store.getters.getRecentRecipes;
|
||||||
|
},
|
||||||
autoResults() {
|
autoResults() {
|
||||||
return this.fuseResults.length > 1 ? this.fuseResults : this.results;
|
return this.fuseResults.length > 1 ? this.fuseResults : this.results;
|
||||||
},
|
},
|
||||||
|
@ -135,7 +135,6 @@ export default {
|
|||||||
this.groupSettings.webhookUrls.splice(index, 1);
|
this.groupSettings.webhookUrls.splice(index, 1);
|
||||||
},
|
},
|
||||||
async saveGroupSettings() {
|
async saveGroupSettings() {
|
||||||
console.log(this.groupSettings);
|
|
||||||
await api.groups.update(this.groupSettings);
|
await api.groups.update(this.groupSettings);
|
||||||
await this.$store.dispatch("requestCurrentGroup");
|
await this.$store.dispatch("requestCurrentGroup");
|
||||||
this.getSiteSettings();
|
this.getSiteSettings();
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DataTable from "@/components/ImportSummaryDialog";
|
import DataTable from "@/components/ImportSummaryDialog/DataTable";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
DataTable,
|
DataTable,
|
||||||
|
@ -223,8 +223,7 @@ export default {
|
|||||||
this.settings.categories.splice(index, 1);
|
this.settings.categories.splice(index, 1);
|
||||||
},
|
},
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
const newSettings = await api.siteSettings.update(this.settings);
|
await api.siteSettings.update(this.settings);
|
||||||
console.log("New Settings", newSettings);
|
|
||||||
this.getOptions();
|
this.getOptions();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
v-if="siteSettings.showRecent"
|
v-if="siteSettings.showRecent"
|
||||||
:title="$t('page.recent')"
|
:title="$t('page.recent')"
|
||||||
:recipes="recentRecipes"
|
:recipes="recentRecipes"
|
||||||
:card-limit="siteSettings.cardsPerSection"
|
:hard-limit="siteSettings.cardsPerSection"
|
||||||
/>
|
/>
|
||||||
<CardSection
|
<CardSection
|
||||||
:sortable="true"
|
:sortable="true"
|
||||||
@ -13,7 +13,7 @@
|
|||||||
:key="section.name + section.position"
|
:key="section.name + section.position"
|
||||||
:title="section.name"
|
:title="section.name"
|
||||||
:recipes="section.recipes"
|
:recipes="section.recipes"
|
||||||
:card-limit="siteSettings.cardsPerSection"
|
:hard-limit="siteSettings.cardsPerSection"
|
||||||
@sort="sortAZ(index)"
|
@sort="sortAZ(index)"
|
||||||
@sort-recent="sortRecent(index)"
|
@sort-recent="sortRecent(index)"
|
||||||
/>
|
/>
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
:sortable="true"
|
:sortable="true"
|
||||||
:title="$t('page.all-recipes')"
|
:title="$t('page.all-recipes')"
|
||||||
:recipes="allRecipes"
|
:recipes="allRecipes"
|
||||||
:card-limit="9999"
|
|
||||||
@sort="sortAZ"
|
@sort="sortAZ"
|
||||||
@sort-recent="sortRecent"
|
@sort-recent="sortRecent"
|
||||||
/>
|
/>
|
||||||
@ -23,6 +22,9 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch("requestAllRecipes");
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
allRecipes() {
|
allRecipes() {
|
||||||
return this.$store.getters.getRecentRecipes;
|
return this.$store.getters.getRecentRecipes;
|
||||||
|
@ -26,7 +26,9 @@
|
|||||||
|
|
||||||
<v-row dense class="mt-0 flex-row align-center justify-space-around">
|
<v-row dense class="mt-0 flex-row align-center justify-space-around">
|
||||||
<v-col>
|
<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" />
|
<FilterSelector class="mb-1" @update="updateCatParams" />
|
||||||
<CategoryTagSelector
|
<CategoryTagSelector
|
||||||
:solo="true"
|
:solo="true"
|
||||||
@ -36,7 +38,9 @@
|
|||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
<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" />
|
<FilterSelector class="mb-1" @update="updateTagParams" />
|
||||||
|
|
||||||
<CategoryTagSelector
|
<CategoryTagSelector
|
||||||
@ -113,6 +117,9 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch("requestAllRecipes");
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
allRecipes() {
|
allRecipes() {
|
||||||
return this.$store.getters.getRecentRecipes;
|
return this.$store.getters.getRecentRecipes;
|
||||||
|
@ -24,6 +24,9 @@ export const routes = [
|
|||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
routes,
|
routes,
|
||||||
mode: process.env.NODE_ENV === "production" ? "history" : "hash",
|
mode: process.env.NODE_ENV === "production" ? "history" : "hash",
|
||||||
|
scrollBehavior() {
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const DEFAULT_TITLE = "Mealie";
|
const DEFAULT_TITLE = "Mealie";
|
||||||
|
@ -53,19 +53,18 @@ const store = new Vuex.Store({
|
|||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async requestRecentRecipes() {
|
async requestRecentRecipes({ getters }) {
|
||||||
// const keys = [
|
const payload = await api.recipes.allSummary(0, 30);
|
||||||
// "name",
|
const recent = getters.getRecentRecipes;
|
||||||
// "slug",
|
if (recent.length >= 30) return;
|
||||||
// "image",
|
|
||||||
// "description",
|
|
||||||
// "dateAdded",
|
|
||||||
// "rating",
|
|
||||||
// ];
|
|
||||||
const payload = await api.recipes.allSummary();
|
|
||||||
|
|
||||||
this.commit("setRecentRecipes", payload);
|
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 }) {
|
async requestCategories({ commit }) {
|
||||||
const categories = await api.categories.getAll();
|
const categories = await api.categories.getAll();
|
||||||
commit("setAllCategories", categories);
|
commit("setAllCategories", categories);
|
||||||
@ -74,7 +73,6 @@ const store = new Vuex.Store({
|
|||||||
const tags = await api.tags.getAll();
|
const tags = await api.tags.getAll();
|
||||||
commit("setAllTags", tags);
|
commit("setAllTags", tags);
|
||||||
},
|
},
|
||||||
|
|
||||||
async requestAppInfo({ commit }) {
|
async requestAppInfo({ commit }) {
|
||||||
const response = await api.meta.getAppInfo();
|
const response = await api.meta.getAppInfo();
|
||||||
commit("setAppInfo", response);
|
commit("setAppInfo", response);
|
||||||
|
@ -16,10 +16,12 @@ class BaseDocument:
|
|||||||
self.schema: BaseModel
|
self.schema: BaseModel
|
||||||
|
|
||||||
# TODO: Improve Get All Query Functionality
|
# 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
|
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]:
|
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
|
"""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")
|
@router.get("/api/recipes/summary")
|
||||||
async def get_recipe_summary(
|
async def get_recipe_summary(
|
||||||
skip=0,
|
start=0,
|
||||||
end=9999,
|
limit=9999,
|
||||||
session: Session = Depends(generate_session),
|
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(
|
def get_all_recipes(
|
||||||
keys: Optional[List[str]] = Query(...),
|
keys: Optional[List[str]] = Query(...),
|
||||||
num: Optional[int] = 100,
|
num: Optional[int] = 100,
|
||||||
@ -54,7 +64,7 @@ def get_all_recipes(
|
|||||||
return db.recipes.get_all_limit_columns(session, keys, limit=num)
|
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)):
|
def get_all_recipes_post(body: AllRecipeRequest, session: Session = Depends(generate_session)):
|
||||||
"""
|
"""
|
||||||
Returns key data for all recipes based off the body data provided.
|
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
|
DEFAULT_GROUP=Home
|
||||||
PRODUCTION=False
|
|
||||||
API_PORT=9000
|
# The Default Credentials for the Super User
|
||||||
API_DOCS=True
|
|
||||||
DB_TYPE=sqlite
|
|
||||||
DEFAULT_EMAIL=changeme@email.com
|
DEFAULT_EMAIL=changeme@email.com
|
||||||
DEFAULT_PASSWORD=MyPassword
|
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_USERNAME=None
|
||||||
SFTP_PASSWORD=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