mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-06-01 04:36:12 -04:00
fix: Shopping List Label Dropdown Doesn't Save Correctly (#2361)
* only update items by label on refresh * made changes more responsive * fast re-order items when labels are re-ordered
This commit is contained in:
parent
fe17922bb8
commit
75698c531a
@ -138,6 +138,10 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
const edit = ref(false);
|
const edit = ref(false);
|
||||||
function toggleEdit(val = !edit.value) {
|
function toggleEdit(val = !edit.value) {
|
||||||
|
if (edit.value === val) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (val) {
|
if (val) {
|
||||||
// update local copy of item with the current value
|
// update local copy of item with the current value
|
||||||
localListItem.value = props.value;
|
localListItem.value = props.value;
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
<div v-for="(value, key, idx) in itemsByLabel" :key="key" class="mb-6">
|
<div v-for="(value, key, idx) in itemsByLabel" :key="key" class="mb-6">
|
||||||
<div @click="toggleShowChecked()">
|
<div @click="toggleShowChecked()">
|
||||||
<span v-if="idx || key !== $tc('shopping-list.no-label')">
|
<span v-if="idx || key !== $tc('shopping-list.no-label')">
|
||||||
<v-icon :color="value[0].label.color">
|
<v-icon :color="getLabelColor(value[0])">
|
||||||
{{ $globals.icons.tags }}
|
{{ $globals.icons.tags }}
|
||||||
</v-icon>
|
</v-icon>
|
||||||
</span>
|
</span>
|
||||||
@ -206,14 +206,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import draggable from "vuedraggable";
|
import draggable from "vuedraggable";
|
||||||
|
|
||||||
import { defineComponent, useAsync, useRoute, computed, ref, watch, onUnmounted, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, useRoute, computed, ref, onUnmounted, useContext } from "@nuxtjs/composition-api";
|
||||||
import { useIdle, useToggle } from "@vueuse/core";
|
import { useIdle, useToggle } from "@vueuse/core";
|
||||||
import { useCopyList } from "~/composables/use-copy";
|
import { useCopyList } from "~/composables/use-copy";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { useAsyncKey } from "~/composables/use-utils";
|
|
||||||
import MultiPurposeLabelSection from "~/components/Domain/ShoppingList/MultiPurposeLabelSection.vue"
|
import MultiPurposeLabelSection from "~/components/Domain/ShoppingList/MultiPurposeLabelSection.vue"
|
||||||
import ShoppingListItem from "~/components/Domain/ShoppingList/ShoppingListItem.vue";
|
import ShoppingListItem from "~/components/Domain/ShoppingList/ShoppingListItem.vue";
|
||||||
import { ShoppingListItemCreate, ShoppingListItemOut, ShoppingListMultiPurposeLabelOut } from "~/lib/api/types/group";
|
import { ShoppingListItemCreate, ShoppingListItemOut, ShoppingListMultiPurposeLabelOut, ShoppingListOut } from "~/lib/api/types/group";
|
||||||
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
||||||
import ShoppingListItemEditor from "~/components/Domain/ShoppingList/ShoppingListItemEditor.vue";
|
import ShoppingListItemEditor from "~/components/Domain/ShoppingList/ShoppingListItemEditor.vue";
|
||||||
import { useFoodStore, useLabelStore, useUnitStore } from "~/composables/store";
|
import { useFoodStore, useLabelStore, useUnitStore } from "~/composables/store";
|
||||||
@ -253,10 +252,7 @@ export default defineComponent({
|
|||||||
// ===============================================================
|
// ===============================================================
|
||||||
// Shopping List Actions
|
// Shopping List Actions
|
||||||
|
|
||||||
const shoppingList = useAsync(async () => {
|
const shoppingList = ref<ShoppingListOut | null>(null);
|
||||||
return await fetchShoppingList();
|
|
||||||
}, useAsyncKey());
|
|
||||||
|
|
||||||
async function fetchShoppingList() {
|
async function fetchShoppingList() {
|
||||||
const { data } = await userApi.shopping.lists.getOne(id);
|
const { data } = await userApi.shopping.lists.getOne(id);
|
||||||
return data;
|
return data;
|
||||||
@ -270,6 +266,7 @@ export default defineComponent({
|
|||||||
// only update the list with the new value if we're not loading, to prevent UI jitter
|
// only update the list with the new value if we're not loading, to prevent UI jitter
|
||||||
if (!loadingCounter.value) {
|
if (!loadingCounter.value) {
|
||||||
shoppingList.value = newListValue;
|
shoppingList.value = newListValue;
|
||||||
|
updateItemsByLabel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,6 +302,7 @@ export default defineComponent({
|
|||||||
// start polling
|
// start polling
|
||||||
loadingCounter.value -= 1;
|
loadingCounter.value -= 1;
|
||||||
const pollFrequency = 5000;
|
const pollFrequency = 5000;
|
||||||
|
pollForChanges(); // populate initial list
|
||||||
|
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const maxAttempts = 3;
|
const maxAttempts = 3;
|
||||||
@ -421,6 +419,10 @@ export default defineComponent({
|
|||||||
const { units: allUnits } = useUnitStore();
|
const { units: allUnits } = useUnitStore();
|
||||||
const { foods: allFoods } = useFoodStore();
|
const { foods: allFoods } = useFoodStore();
|
||||||
|
|
||||||
|
function getLabelColor(item: ShoppingListItemOut | null) {
|
||||||
|
return item?.label?.color;
|
||||||
|
}
|
||||||
|
|
||||||
function sortByLabels() {
|
function sortByLabels() {
|
||||||
preferences.value.viewByLabel = !preferences.value.viewByLabel;
|
preferences.value.viewByLabel = !preferences.value.viewByLabel;
|
||||||
}
|
}
|
||||||
@ -441,6 +443,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
// setting this doesn't have any effect on the data since it's refreshed automatically, but it makes the ux feel smoother
|
// setting this doesn't have any effect on the data since it's refreshed automatically, but it makes the ux feel smoother
|
||||||
shoppingList.value.labelSettings = labelSettings;
|
shoppingList.value.labelSettings = labelSettings;
|
||||||
|
updateItemsByLabel();
|
||||||
|
|
||||||
loadingCounter.value += 1;
|
loadingCounter.value += 1;
|
||||||
const { data } = await userApi.shopping.lists.updateLabelSettings(shoppingList.value.id, labelSettings);
|
const { data } = await userApi.shopping.lists.updateLabelSettings(shoppingList.value.id, labelSettings);
|
||||||
@ -514,10 +517,6 @@ export default defineComponent({
|
|||||||
itemsByLabel.value = itemsSorted;
|
itemsByLabel.value = itemsSorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(shoppingList, () => {
|
|
||||||
updateItemsByLabel();
|
|
||||||
}, {deep: true});
|
|
||||||
|
|
||||||
async function refreshLabels() {
|
async function refreshLabels() {
|
||||||
const { data } = await userApi.multiPurposeLabels.getAll();
|
const { data } = await userApi.multiPurposeLabels.getAll();
|
||||||
|
|
||||||
@ -587,18 +586,29 @@ export default defineComponent({
|
|||||||
// make sure the item is at the end of the list with the other checked items
|
// make sure the item is at the end of the list with the other checked items
|
||||||
item.position = shoppingList.value.listItems.length;
|
item.position = shoppingList.value.listItems.length;
|
||||||
|
|
||||||
// set a temporary updatedAt timestamp so it appears at the top of the checked items in the UI
|
// set a temporary updatedAt timestamp prior to refresh so it appears at the top of the checked items
|
||||||
item.updateAt = new Date().toISOString();
|
item.updateAt = new Date().toISOString();
|
||||||
item.updateAt = item.updateAt.substring(0, item.updateAt.length-1);
|
item.updateAt = item.updateAt.substring(0, item.updateAt.length-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make updates reflect immediately
|
||||||
|
if (shoppingList.value.listItems) {
|
||||||
|
shoppingList.value.listItems.forEach((oldListItem: ShoppingListItemOut, idx: number) => {
|
||||||
|
if (oldListItem.id === item.id && shoppingList.value?.listItems) {
|
||||||
|
shoppingList.value.listItems[idx] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateItemsByLabel();
|
||||||
|
|
||||||
loadingCounter.value += 1;
|
loadingCounter.value += 1;
|
||||||
const { data } = await userApi.shopping.items.updateOne(item.id, item);
|
const { data } = await userApi.shopping.items.updateOne(item.id, item);
|
||||||
loadingCounter.value -= 1;
|
loadingCounter.value -= 1;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteListItem(item: ShoppingListItemOut) {
|
async function deleteListItem(item: ShoppingListItemOut) {
|
||||||
@ -728,6 +738,7 @@ export default defineComponent({
|
|||||||
deleteChecked,
|
deleteChecked,
|
||||||
deleteListItem,
|
deleteListItem,
|
||||||
edit,
|
edit,
|
||||||
|
getLabelColor,
|
||||||
itemsByLabel,
|
itemsByLabel,
|
||||||
listItems,
|
listItems,
|
||||||
listRecipes,
|
listRecipes,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user