mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-11-03 18:53:17 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			195 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			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,
 | 
						|
  };
 | 
						|
}
 |