import 'dart:async'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; import 'package:get/get.dart'; import '../../db/database.dart'; import '../../services/db_service.dart'; import '../../services/toaster_service.dart'; import '../../utils/get_interface_extension.dart'; import 'dialogs/category_manager_dialog.dart'; import 'dialogs/product_editor_dialog.dart'; import 'dialogs/shopping_item_editor_dialog.dart'; import 'dialogs/storage_location_editor_dialog.dart'; import 'dialogs/user_manager_dialog.dart'; class HomeController extends GetxController { final soonExpiries = <(ProductData, ProductCategoryData, StorageLocationData)>[].obs; final stockProducts = <(StorageLocationData, List<(ProductData, ProductCategoryData)>)>[].obs; final shoppingList = <(ShoppingListItemData, ProductCategoryData, StorageLocationData)>[].obs; final groupedShoppingList = >{}.obs; final products = [].obs; final categories = [].obs; final storages = [].obs; late final StreamSubscription psSub; late final StreamSubscription seSub; late final StreamSubscription slSub; late final StreamSubscription pcSub; final expandedStorages = [].obs; late final Rx user; @override void onInit() { super.onInit(); refreshAll(); psSub = DBService.to.db.productsSubscription.listen((data) { refreshStockProducts(); }); seSub = DBService.to.db.soonExpirySubscription.listen((data) { refreshSoonExpiries(); }); slSub = DBService.to.db.shoppingListSubscription.listen((data) { refreshShoppingList(); }); pcSub = DBService.to.db.productsCategoriesSubscription.listen((data) { refreshCategoriesList(); }); } @override void onClose() { psSub.cancel(); seSub.cancel(); slSub.cancel(); pcSub.cancel(); super.onClose(); } Future refreshAll() async { await refreshCategoriesList(); await refreshStockProducts(); await refreshSoonExpiries(); await refreshShoppingList(); user.value = await DBService.to.db.getUser(user.value); } Future refreshSoonExpiries() async { final sExps = await DBService.to.db.getSoonExpiryProducts(); print(sExps); soonExpiries.clear(); soonExpiries.addAll(sExps); } Future refreshStockProducts() async { final stock = await DBService.to.db.getStorageLocations(); storages.clear(); storages.addAll(stock.map((e) => e.$1)); final expandedDiff = expandedStorages.length - storages.length; if (expandedDiff < 0) { expandedStorages.addAll(List.generate(expandedDiff.abs(), (idx) => true)); } else if (expandedDiff > 0) { expandedStorages.value = expandedStorages.sublist( 0, expandedStorages.length - expandedDiff, ); } products.clear(); products.addAll(stock.map((e) => e.$2).flattened.map((e) => e.$1)); stockProducts.clear(); stockProducts.addAll(stock); } Future refreshShoppingList() async { final stock = await DBService.to.db.getShoppingList(); shoppingList.clear(); shoppingList.addAll(stock); groupedShoppingList.clear(); groupedShoppingList[true] = []; groupedShoppingList[false] = []; for (var e in shoppingList) { groupedShoppingList[e.$1.isPurchased]!.add(e); } } Future refreshCategoriesList() async { final categories = await DBService.to.db.getProductCategories(); this.categories.clear(); this.categories.addAll(categories); } void addNewProduct([StorageLocationData? psld]) { ProductEditorDialog.show( categories: categories, storages: storages, preselectedStorage: psld, ); } Future editProduct(ProductData prod) async { await ProductEditorDialog.show( categories: categories, storages: storages, editedProduct: prod, ); refreshAll(); } Future deleteProduct(ProductData prod) async { final confirmed = await Get.confirm( title: "Внимание!", content: "Удаление продукта необратимо!\n" "Точно хотите удалить '${prod.name} - ${prod.quantity} ${prod.unit}'?", ); if (!confirmed) return; await DBService.to.db.deleteProduct(prod); ToasterService.to.success( title: "Успех", message: "Продукт '${prod.name}' удалён", ); refreshAll(); } Future editStorageLocation(StorageLocationData storage) async { await StorageLocationEditorDialog.show(esl: storage); refreshAll(); } Future deleteStorageLocation(StorageLocationData storage) async { final confirmed = await Get.confirm( title: "Внимание!", content: "Удаление места хранения необратимо!\n" "Точно хотите удалить '${storage.name}'?", ); if (!confirmed) return; for (final (storageItem, items) in stockProducts) { if (storageItem.id == storage.id && items.isNotEmpty) { ToasterService.to.error( title: "Ошибка", message: "Нельзя удалить место хранения, к которому привязаны предметы!", ); return; } } await DBService.to.db.deleteStorageLocation(storage); ToasterService.to.success( title: "Успех", message: "Место хранения '${storage.name}' удалено", ); refreshAll(); } Future addNewShoppingItem([ProductData? ppd]) async { await ShoppingItemEditorDialog.show( categories: categories, storages: storages, ); refreshShoppingList(); } Future switchShoppingItem( ShoppingListItemData item, bool isPurchased, ) async { await DBService.to.db.updateShoppingListItem( id: item.id, isPurchased: isPurchased, ); refreshShoppingList(); } Future editShoppingItem( ShoppingListItemData item, ProductCategoryData category, StorageLocationData location, ) async { await ShoppingItemEditorDialog.show( categories: categories, storages: storages, editedItem: (item, category, location), ); refreshShoppingList(); } Future deleteShoppingItem(ShoppingListItemData item) async { final confirmed = await Get.confirm( title: "Внимание!", content: "Удаление записи из списка покупок необратимо!\n" "Точно хотите удалить '${item.name} - ${item.quantity} ${item.unit}'?", ); if (!confirmed) return; await DBService.to.db.deleteShoppingListItem(item); refreshShoppingList(); } Future saveShoppingItem(ShoppingListItemData item, ProductCategoryData category, StorageLocationData location) async { final expiryDate = await showDatePicker( context: Get.context!, helpText: "Срок годности продукта из списка покупок", initialDate: DateTime.now().add(1.days), firstDate: DateTime.now(), lastDate: DateTime(9999), ); if (expiryDate == null) { return; } await DBService.to.db.deleteShoppingListItem(item); await DBService.to.db.addProduct( name: item.name, category: category, storage: location, quantity: item.quantity, unit: item.unit, expiryDate: expiryDate, barcode: "", ); ToasterService.to.success( title: "Успех", message: "Продукт '${item.name}' перемещён " "из списка покупок в список продуктов", ); } Future addNewStorage() async { await StorageLocationEditorDialog.show(); refreshStockProducts(); } Future addToShoppingList(ProductData p) async { await ShoppingItemEditorDialog.show( categories: categories, storages: storages, fromProduct: p, ); refreshShoppingList(); } void setUser(UserData user) { this.user = Rx(user); } Future openCategoryManagerDialog() async { await CategoryManagerDialog.show(); refreshAll(); } Future openUserManagerDialog() async { await UserManagerDialog.show(); refreshAll(); } Future logout() async { Get.offAllNamed("/login"); Get.delete(force: true); } }