Files
mealie/frontend/composables/shopping-list-page/use-shopping-list-page.ts

195 lines
5.3 KiB
TypeScript

import type { ShoppingListItemOut } from "~/lib/api/types/household";
import { useShoppingListState } from "~/composables/shopping-list-page/sub-composables/use-shopping-list-state";
import { useShoppingListData } from "~/composables/shopping-list-page/sub-composables/use-shopping-list-data";
import { useShoppingListSorting } from "~/composables/shopping-list-page/sub-composables/use-shopping-list-sorting";
import { useShoppingListLabels } from "~/composables/shopping-list-page/sub-composables/use-shopping-list-labels";
import { useShoppingListCopy } from "~/composables/shopping-list-page/sub-composables/use-shopping-list-copy";
import { useShoppingListCrud } from "~/composables/shopping-list-page/sub-composables/use-shopping-list-crud";
import { useShoppingListRecipes } from "~/composables/shopping-list-page/sub-composables/use-shopping-list-recipes";
/**
* Main composable that orchestrates all shopping list page functionality
*/
export function useShoppingListPage(listId: string) {
// Initialize state
const state = useShoppingListState();
const {
shoppingList,
loadingCounter,
recipeReferenceLoading,
preserveItemOrder,
listItems,
sortCheckedItems,
} = state;
// Initialize sorting functionality
const sorting = useShoppingListSorting();
const { groupAndSortListItemsByFood, sortListItems, updateItemsByLabel } = sorting;
// Track items organized by label
const itemsByLabel = ref<{ [key: string]: ShoppingListItemOut[] }>({});
function updateListItemOrder() {
if (!shoppingList.value) return;
if (!preserveItemOrder.value) {
groupAndSortListItemsByFood(shoppingList.value);
}
else {
sortListItems(shoppingList.value);
}
const labeledItems = updateItemsByLabel(shoppingList.value);
if (labeledItems) {
itemsByLabel.value = labeledItems;
}
}
// Initialize data management
const dataManager = useShoppingListData(listId, shoppingList, loadingCounter);
const { isOffline, refresh: baseRefresh, startPolling, stopPolling, shoppingListItemActions } = dataManager;
const refresh = () => baseRefresh(updateListItemOrder);
// Initialize shopping list labels
const labels = useShoppingListLabels(shoppingList);
// Initialize copy functionality
const copyManager = useShoppingListCopy();
// Initialize CRUD operations
const crud = useShoppingListCrud(
shoppingList,
loadingCounter,
listItems,
shoppingListItemActions,
refresh,
sortCheckedItems,
updateListItemOrder,
);
// Initialize recipe management
const recipes = useShoppingListRecipes(
shoppingList,
loadingCounter,
recipeReferenceLoading,
refresh,
);
// Handle item reordering by label
function updateIndexUncheckedByLabel(labelName: string, labeledUncheckedItems: ShoppingListItemOut[]) {
if (!itemsByLabel.value[labelName]) {
return;
}
// update this label's item order
itemsByLabel.value[labelName] = labeledUncheckedItems;
// reset list order of all items
const allUncheckedItems: ShoppingListItemOut[] = [];
for (const labelKey in itemsByLabel.value) {
allUncheckedItems.push(...itemsByLabel.value[labelKey]);
}
// since the user has manually reordered the list, we should preserve this order
preserveItemOrder.value = true;
// save changes
listItems.unchecked = allUncheckedItems;
listItems.checked = shoppingList.value?.listItems?.filter(item => item.checked) || [];
crud.updateUncheckedListItems();
}
// Dialog helpers
function openCheckAll() {
if (shoppingList.value?.listItems?.some(item => !item.checked)) {
state.state.checkAllDialog = true;
}
}
function openUncheckAll() {
if (shoppingList.value?.listItems?.some(item => item.checked)) {
state.state.uncheckAllDialog = true;
}
}
function openDeleteChecked() {
if (shoppingList.value?.listItems?.some(item => item.checked)) {
state.state.deleteCheckedDialog = true;
}
}
function checkAll() {
state.state.checkAllDialog = false;
crud.checkAllItems();
}
function uncheckAll() {
state.state.uncheckAllDialog = false;
crud.uncheckAllItems();
}
function deleteChecked() {
state.state.deleteCheckedDialog = false;
crud.deleteCheckedItems();
}
// Copy functionality wrapper
function copyListItems(copyType: "plain" | "markdown") {
copyManager.copyListItems(itemsByLabel.value, copyType);
}
// Label reordering helpers
function toggleReorderLabelsDialog() {
crud.toggleReorderLabelsDialog(state.reorderLabelsDialog);
}
async function saveLabelOrder() {
await crud.saveLabelOrder(() => {
const labeledItems = updateItemsByLabel(shoppingList.value!);
if (labeledItems) {
itemsByLabel.value = labeledItems;
}
});
}
// Lifecycle management
onMounted(() => {
startPolling(updateListItemOrder);
});
onUnmounted(() => {
stopPolling();
});
return {
itemsByLabel,
isOffline,
// Sub-composables
...state,
...labels,
...crud,
...recipes,
// Specialized functions
updateIndexUncheckedByLabel,
copyListItems,
// Dialog actions
openCheckAll,
openUncheckAll,
openDeleteChecked,
checkAll,
uncheckAll,
deleteChecked,
// Label management
toggleReorderLabelsDialog,
saveLabelOrder,
// Data refresh
refresh,
};
}