mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-30 17:53:31 -04: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, | ||
|  |   }; | ||
|  | } |