mirror of
https://github.com/hay-kot/mealie.git
synced 2025-07-12 08:07:14 -07:00
feat: implement local storage for sorting and dynamic sort icons on the new recipe sort card (#1506)
* added new sort icons * added dynamic sort icons * implemented local storage for sorting and mobile card view * fixed bug with local storage booleans * added type hints * bum vue use to use merge defaults * use reactive localstorage * add $vuetify type * sort returns * fix type error Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
This commit is contained in:
parent
34f52c06a6
commit
1b83c82997
7 changed files with 133 additions and 66 deletions
|
@ -61,7 +61,7 @@
|
|||
<template #activator="{ on, attrs }">
|
||||
<v-btn text :icon="$vuetify.breakpoint.xsOnly" v-bind="attrs" :loading="sortLoading" v-on="on">
|
||||
<v-icon :left="!$vuetify.breakpoint.xsOnly">
|
||||
{{ $globals.icons.sort }}
|
||||
{{ preferences.sortIcon }}
|
||||
</v-icon>
|
||||
{{ $vuetify.breakpoint.xsOnly ? null : $t("general.sort") }}
|
||||
</v-btn>
|
||||
|
@ -102,11 +102,11 @@
|
|||
event: 'toggle-dense-view',
|
||||
},
|
||||
]"
|
||||
@toggle-dense-view="mobileCards = !mobileCards"
|
||||
@toggle-dense-view="toggleMobileCards()"
|
||||
/>
|
||||
</v-app-bar>
|
||||
<div v-if="recipes" class="mt-2">
|
||||
<v-row v-if="!viewScale">
|
||||
<v-row v-if="!useMobileCards">
|
||||
<v-col v-for="(recipe, index) in recipes" :key="recipe.slug + index" :sm="6" :md="6" :lg="4" :xl="3">
|
||||
<v-lazy>
|
||||
<RecipeCard
|
||||
|
@ -174,6 +174,7 @@ import RecipeCardMobile from "./RecipeCardMobile.vue";
|
|||
import { useAsyncKey } from "~/composables/use-utils";
|
||||
import { useLazyRecipes, useSorter } from "~/composables/recipes";
|
||||
import { Recipe } from "~/types/api-types/recipe";
|
||||
import { useUserSortPreferences } from "~/composables/use-users/preferences";
|
||||
|
||||
const SORT_EVENT = "sort";
|
||||
const REPLACE_RECIPES_EVENT = "replaceRecipes";
|
||||
|
@ -211,7 +212,8 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
const mobileCards = ref(false);
|
||||
const preferences = useUserSortPreferences();
|
||||
|
||||
const utils = useSorter();
|
||||
|
||||
const EVENTS = {
|
||||
|
@ -223,8 +225,8 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
const { $globals, $vuetify } = useContext();
|
||||
const viewScale = computed(() => {
|
||||
return mobileCards.value || $vuetify.breakpoint.smAndDown;
|
||||
const useMobileCards = computed(() => {
|
||||
return $vuetify.breakpoint.smAndDown || preferences.value.useMobileCards;
|
||||
});
|
||||
|
||||
const displayTitleIcon = computed(() => {
|
||||
|
@ -247,18 +249,20 @@ export default defineComponent({
|
|||
|
||||
const page = ref(1);
|
||||
const perPage = ref(30);
|
||||
const orderBy = ref("name");
|
||||
const orderDirection = ref("asc");
|
||||
const hasMore = ref(true);
|
||||
|
||||
const ready = ref(false);
|
||||
const loading = ref(false);
|
||||
|
||||
const { recipes, fetchMore } = useLazyRecipes();
|
||||
const { fetchMore } = useLazyRecipes();
|
||||
|
||||
onMounted(async () => {
|
||||
if (props.usePagination) {
|
||||
const newRecipes = await fetchMore(page.value, perPage.value, orderBy.value, orderDirection.value);
|
||||
const newRecipes = await fetchMore(
|
||||
page.value,
|
||||
perPage.value,
|
||||
preferences.value.orderBy,
|
||||
preferences.value.orderDirection
|
||||
);
|
||||
context.emit(REPLACE_RECIPES_EVENT, newRecipes);
|
||||
ready.value = true;
|
||||
}
|
||||
|
@ -273,7 +277,12 @@ export default defineComponent({
|
|||
loading.value = true;
|
||||
page.value = page.value + 1;
|
||||
|
||||
const newRecipes = await fetchMore(page.value, perPage.value, orderBy.value, orderDirection.value);
|
||||
const newRecipes = await fetchMore(
|
||||
page.value,
|
||||
perPage.value,
|
||||
preferences.value.orderBy,
|
||||
preferences.value.orderDirection
|
||||
);
|
||||
if (!newRecipes.length) {
|
||||
hasMore.value = false;
|
||||
} else {
|
||||
|
@ -284,51 +293,39 @@ export default defineComponent({
|
|||
}, useAsyncKey());
|
||||
}, 500);
|
||||
|
||||
/*
|
||||
sortRecipes helps filter using the API. This will eventually replace the sortRecipesFrontend function which pulls all recipes
|
||||
(without pagination) and does the sorting in the frontend.
|
||||
|
||||
TODO: remove sortRecipesFrontend and remove duplicate "sortRecipes" section in the template (above)
|
||||
TODO: use indicator to show asc / desc order
|
||||
*/
|
||||
|
||||
/**
|
||||
* sortRecipes helps filter using the API. This will eventually replace the sortRecipesFrontend function which pulls all recipes
|
||||
* (without pagination) and does the sorting in the frontend.
|
||||
* TODO: remove sortRecipesFrontend and remove duplicate "sortRecipes" section in the template (above)
|
||||
* @param sortType
|
||||
*/
|
||||
function sortRecipes(sortType: string) {
|
||||
if (state.sortLoading || loading.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
function setter(orderBy: string, ascIcon: string, descIcon: string) {
|
||||
if (preferences.value.orderBy !== orderBy) {
|
||||
preferences.value.orderBy = orderBy;
|
||||
preferences.value.orderDirection = "asc";
|
||||
} else {
|
||||
preferences.value.orderDirection = preferences.value.orderDirection === "asc" ? "desc" : "asc";
|
||||
}
|
||||
preferences.value.sortIcon = preferences.value.orderDirection === "asc" ? ascIcon : descIcon;
|
||||
}
|
||||
|
||||
switch (sortType) {
|
||||
case EVENTS.az:
|
||||
if (orderBy.value !== "name") {
|
||||
orderBy.value = "name";
|
||||
orderDirection.value = "asc";
|
||||
} else {
|
||||
orderDirection.value = orderDirection.value === "asc" ? "desc" : "asc";
|
||||
}
|
||||
setter("name", $globals.icons.sortAlphabeticalAscending, $globals.icons.sortAlphabeticalDescending);
|
||||
break;
|
||||
case EVENTS.rating:
|
||||
if (orderBy.value !== "rating") {
|
||||
orderBy.value = "rating";
|
||||
orderDirection.value = "desc";
|
||||
} else {
|
||||
orderDirection.value = orderDirection.value === "asc" ? "desc" : "asc";
|
||||
}
|
||||
setter("rating", $globals.icons.sortAscending, $globals.icons.sortDescending);
|
||||
break;
|
||||
case EVENTS.created:
|
||||
if (orderBy.value !== "created_at") {
|
||||
orderBy.value = "created_at";
|
||||
orderDirection.value = "desc";
|
||||
} else {
|
||||
orderDirection.value = orderDirection.value === "asc" ? "desc" : "asc";
|
||||
}
|
||||
setter("created_at", $globals.icons.sortCalendarAscending, $globals.icons.sortCalendarDescending);
|
||||
break;
|
||||
case EVENTS.updated:
|
||||
if (orderBy.value !== "update_at") {
|
||||
orderBy.value = "update_at";
|
||||
orderDirection.value = "desc";
|
||||
} else {
|
||||
orderDirection.value = orderDirection.value === "asc" ? "desc" : "asc";
|
||||
}
|
||||
setter("updated_at", $globals.icons.sortClockAscending, $globals.icons.sortClockDescending);
|
||||
break;
|
||||
default:
|
||||
console.log("Unknown Event", sortType);
|
||||
|
@ -344,7 +341,12 @@ export default defineComponent({
|
|||
loading.value = true;
|
||||
|
||||
// fetch new recipes
|
||||
const newRecipes = await fetchMore(page.value, perPage.value, orderBy.value, orderDirection.value);
|
||||
const newRecipes = await fetchMore(
|
||||
page.value,
|
||||
perPage.value,
|
||||
preferences.value.orderBy,
|
||||
preferences.value.orderDirection
|
||||
);
|
||||
context.emit(REPLACE_RECIPES_EVENT, newRecipes);
|
||||
|
||||
state.sortLoading = false;
|
||||
|
@ -379,17 +381,22 @@ export default defineComponent({
|
|||
state.sortLoading = false;
|
||||
}
|
||||
|
||||
function toggleMobileCards() {
|
||||
preferences.value.useMobileCards = !preferences.value.useMobileCards;
|
||||
}
|
||||
|
||||
return {
|
||||
mobileCards,
|
||||
...toRefs(state),
|
||||
EVENTS,
|
||||
viewScale,
|
||||
displayTitleIcon,
|
||||
EVENTS,
|
||||
infiniteScroll,
|
||||
loading,
|
||||
navigateRandom,
|
||||
preferences,
|
||||
sortRecipes,
|
||||
sortRecipesFrontend,
|
||||
toggleMobileCards,
|
||||
useMobileCards,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue