inventoryView 로직 분리
This commit is contained in:
parent
f75ec7684f
commit
5642a5fe77
@ -27,8 +27,9 @@ public abstract class BaseUi : MonoBehaviour
|
||||
protected GameObject _panel;
|
||||
protected BindingContext _bindingContext;
|
||||
|
||||
public virtual bool IsBlockingTime => false;
|
||||
public virtual bool IsOpen => _panel != null && _panel.activeSelf;
|
||||
public bool IsInitialized { get; protected set; }
|
||||
public bool IsBlockingTime => false;
|
||||
public bool IsOpen => _panel != null && _panel.activeSelf;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
@ -100,6 +101,7 @@ public virtual void ClosePanel()
|
||||
}
|
||||
|
||||
_panel.SetActive(false);
|
||||
IsInitialized = false;
|
||||
}
|
||||
|
||||
public virtual void SetUiInteractable(bool active)
|
||||
|
@ -26,7 +26,7 @@ protected override void Update()
|
||||
base.Update();
|
||||
|
||||
// BasePopupUi의 Update 로직 구현
|
||||
if (IsOpenPanel() == false) return;
|
||||
if (IsOpenPanel() == false || IsInitialized == false) return;
|
||||
|
||||
var currentSelectedGameObject = EventSystem.current.currentSelectedGameObject;
|
||||
if (!currentSelectedGameObject || currentSelectedGameObject.activeInHierarchy == false)
|
||||
|
@ -1,167 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
/// <summary>
|
||||
/// 인벤토리 관련 비즈니스 로직을 담당하는 서비스
|
||||
/// 기존 InventoryView의 로직을 서비스 계층으로 분리
|
||||
/// </summary>
|
||||
public class InventoryService : IUiService
|
||||
{
|
||||
private RestaurantManagementData _restaurantManagementData;
|
||||
private RestaurantManagementState _restaurantManagementState;
|
||||
|
||||
/// <summary>
|
||||
/// 서비스 초기화
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
_restaurantManagementState = RestaurantState.Instance.ManagementState;
|
||||
_restaurantManagementData = RestaurantData.Instance.ManagementData;
|
||||
|
||||
Debug.Assert(_restaurantManagementData != null, "RestaurantManagementData is null");
|
||||
Debug.Assert(_restaurantManagementState != null, "RestaurantManagementState is null");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 서비스 정리
|
||||
/// </summary>
|
||||
public void Cleanup()
|
||||
{
|
||||
// 필요한 경우 리소스 정리
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI 상태 업데이트
|
||||
/// </summary>
|
||||
public void UpdateUiState()
|
||||
{
|
||||
// UI 상태 업데이트 로직
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 인벤토리 아이템 목록 가져오기
|
||||
/// </summary>
|
||||
/// <returns>인벤토리 아이템 ViewModel 목록</returns>
|
||||
public List<ItemViewModel> GetInventoryItems()
|
||||
{
|
||||
return ItemViewModelFactory.CreateRestaurantManagementInventoryItem();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리와 정렬 타입에 따라 필터링된 아이템 목록 가져오기
|
||||
/// </summary>
|
||||
/// <param name="category">카테고리 필터</param>
|
||||
/// <param name="sortType">정렬 타입</param>
|
||||
/// <returns>필터링 및 정렬된 아이템 목록</returns>
|
||||
public IEnumerable<ItemViewModel> FilterItems(InventoryCategoryType category, InventorySortType sortType)
|
||||
{
|
||||
var items = GetInventoryItems();
|
||||
|
||||
// 카테고리 필터링
|
||||
var filtered = items.Where(item => MatchesCategory(item, category));
|
||||
|
||||
// 정렬
|
||||
return ApplySort(filtered, sortType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 오늘의 메뉴에 등록되지 않은 아이템들만 필터링
|
||||
/// </summary>
|
||||
/// <param name="items">원본 아이템 목록</param>
|
||||
/// <returns>오늘의 메뉴에 등록되지 않은 아이템들</returns>
|
||||
public IEnumerable<ItemViewModel> FilterOutTodayMenuItems(IEnumerable<ItemViewModel> items)
|
||||
{
|
||||
return items.Where(item =>
|
||||
!(item.ItemType == ItemType.Recipe && _restaurantManagementState.IsContainTodayMenu(item.Id)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이템이 특정 카테고리와 일치하는지 확인
|
||||
/// </summary>
|
||||
/// <param name="item">확인할 아이템</param>
|
||||
/// <param name="category">카테고리</param>
|
||||
/// <returns>일치 여부</returns>
|
||||
public bool MatchesCategory(ItemViewModel item, InventoryCategoryType category)
|
||||
{
|
||||
switch (category)
|
||||
{
|
||||
case InventoryCategoryType.Food:
|
||||
if (item.ItemType != ItemType.Recipe) return false;
|
||||
return DataManager.Instance.GetDataSo<RecipeDataSo>()
|
||||
.TryGetDataById(item.Id, out var foodRecipe) && foodRecipe.RecipeType == RecipeType.FoodRecipe;
|
||||
|
||||
case InventoryCategoryType.Drink:
|
||||
if (item.ItemType != ItemType.Recipe) return false;
|
||||
return DataManager.Instance.GetDataSo<RecipeDataSo>()
|
||||
.TryGetDataById(item.Id, out var drinkRecipe) && drinkRecipe.RecipeType == RecipeType.DrinkRecipe;
|
||||
|
||||
case InventoryCategoryType.Ingredient:
|
||||
return item.ItemType == ItemType.Ingredient;
|
||||
|
||||
case InventoryCategoryType.Cookware:
|
||||
return DataManager.Instance.GetDataSo<CookwareDataSo>()
|
||||
.TryGetDataById(item.Id, out var cookwareData);
|
||||
|
||||
case InventoryCategoryType.Special:
|
||||
return false; // 특수 아이템 로직 추가 필요
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 정렬 타입에 따라 아이템 목록 정렬
|
||||
/// </summary>
|
||||
/// <param name="items">정렬할 아이템 목록</param>
|
||||
/// <param name="sortType">정렬 타입</param>
|
||||
/// <returns>정렬된 아이템 목록</returns>
|
||||
public IEnumerable<ItemViewModel> ApplySort(IEnumerable<ItemViewModel> items, InventorySortType sortType)
|
||||
{
|
||||
return sortType switch
|
||||
{
|
||||
InventorySortType.NameAscending => items.OrderByDescending(item => item.HasItem).ThenBy(item => item.DisplayName),
|
||||
InventorySortType.NameDescending => items.OrderByDescending(item => item.HasItem).ThenByDescending(item => item.DisplayName),
|
||||
InventorySortType.QuantityAscending => items.OrderByDescending(item => item.HasItem).ThenBy(item => item.Count),
|
||||
InventorySortType.QuantityDescending => items.OrderByDescending(item => item.HasItem).ThenByDescending(item => item.Count),
|
||||
InventorySortType.None => items.OrderBy(item => item.Id),
|
||||
_ => items
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이템이 아이템을 보유하고 있는지 확인
|
||||
/// </summary>
|
||||
/// <param name="items">확인할 아이템 목록</param>
|
||||
/// <returns>아이템을 보유한 목록</returns>
|
||||
public IEnumerable<ItemViewModel> GetItemsWithStock(IEnumerable<ItemViewModel> items)
|
||||
{
|
||||
return items.Where(item => item.HasItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리별 아이템 개수 가져오기
|
||||
/// </summary>
|
||||
/// <param name="category">카테고리</param>
|
||||
/// <returns>해당 카테고리의 보유 아이템 수</returns>
|
||||
public int GetItemCountByCategory(InventoryCategoryType category)
|
||||
{
|
||||
var filteredItems = FilterItems(category, InventorySortType.None);
|
||||
var itemsWithStock = GetItemsWithStock(filteredItems);
|
||||
return itemsWithStock.Count();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 전체 인벤토리에서 보유한 아이템 총 개수
|
||||
/// </summary>
|
||||
/// <returns>보유 아이템 총 개수</returns>
|
||||
public int GetTotalItemCount()
|
||||
{
|
||||
var allItems = GetInventoryItems();
|
||||
return GetItemsWithStock(allItems).Count();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 682535d5577b54c499b1f52b4177d202
|
@ -1,6 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
@ -8,158 +5,41 @@ namespace DDD
|
||||
public class InventoryView : MonoBehaviour, IEventHandler<InventoryChangedEvent>,
|
||||
IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
|
||||
{
|
||||
[SerializeField] private Transform _slotParent;
|
||||
|
||||
private RestaurantManagementData restaurantManagementDataSo;
|
||||
private RestaurantManagementState restaurantManagementStateSo;
|
||||
private InventoryCategoryType _currenInventoryCategoryType = InventoryCategoryType.Food;
|
||||
|
||||
private readonly Dictionary<string, ItemSlotUi> _slotLookup = new();
|
||||
private GameObject _firstSlot;
|
||||
private InventorySortType _currentSortType = InventorySortType.None;
|
||||
|
||||
private const string ItemSlotUiName = "ItemSlotUi_";
|
||||
private RestaurantManagementViewModel _viewModel;
|
||||
|
||||
public GameObject GetInitialSelected() => _firstSlot;
|
||||
[SerializeField] private Transform _slotParent;
|
||||
|
||||
public GameObject GetInitialSelected() => _viewModel.GetInitialSelectedByInventory();
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
EventBus.Register<InventoryChangedEvent>(this);
|
||||
EventBus.Register<TodayMenuAddedEvent>(this);
|
||||
EventBus.Register<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
private void OnDestroy()
|
||||
{
|
||||
EventBus.Unregister<InventoryChangedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuAddedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
public void Initialize(RestaurantManagementViewModel viewModel)
|
||||
{
|
||||
restaurantManagementStateSo = RestaurantState.Instance.ManagementState;
|
||||
restaurantManagementDataSo = RestaurantData.Instance.ManagementData;
|
||||
Debug.Assert(restaurantManagementDataSo != null, "_todayMenuDataSo != null");
|
||||
_viewModel = viewModel;
|
||||
|
||||
Clear();
|
||||
|
||||
var models = ItemViewModelFactory.CreateRestaurantManagementInventoryItem();
|
||||
|
||||
_slotLookup.Clear();
|
||||
foreach (var model in models)
|
||||
{
|
||||
var itemSlotUi = Instantiate(restaurantManagementDataSo.ItemSlotUiPrefab, _slotParent);
|
||||
var slot = itemSlotUi.GetComponent<ItemSlotUi>();
|
||||
slot.Initialize(model, new InventorySlotUiStrategy());
|
||||
itemSlotUi.name = ItemSlotUiName + model.Id;
|
||||
|
||||
var interactor = itemSlotUi.GetComponent<ItemSlotInteractor>();
|
||||
if (model.ItemType == ItemType.Recipe)
|
||||
{
|
||||
interactor.Initialize(TodayMenuEventType.Add, new TodayMenuInteractorStrategy());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DataManager.Instance.GetDataSo<CookwareDataSo>().TryGetDataById(model.Id, out var cookwareData))
|
||||
{
|
||||
interactor.Initialize(TodayMenuEventType.Add, new TodayCookwareInteractorStrategy());
|
||||
}
|
||||
}
|
||||
|
||||
_slotLookup[model.Id] = slot;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSortType(InventorySortType sortType)
|
||||
{
|
||||
_currentSortType = sortType;
|
||||
UpdateView();
|
||||
}
|
||||
|
||||
private IEnumerable<ItemSlotUi> SortSlots(IEnumerable<ItemSlotUi> slots)
|
||||
{
|
||||
return _currentSortType switch
|
||||
{
|
||||
InventorySortType.NameAscending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenBy(slot => slot.Model.DisplayName),
|
||||
InventorySortType.NameDescending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenByDescending(slot => slot.Model.DisplayName),
|
||||
InventorySortType.QuantityAscending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenBy(slot => slot.Model.Count),
|
||||
InventorySortType.QuantityDescending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenByDescending(slot => slot.Model.Count),
|
||||
InventorySortType.None => slots.OrderBy(slot => slot.Model.Id),
|
||||
_ => slots
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateCategoryView(InventoryCategoryType category)
|
||||
{
|
||||
_currenInventoryCategoryType = category;
|
||||
GameObject firstValidSlot = null;
|
||||
|
||||
var filteredSlots = _slotLookup.Values;
|
||||
var sortedSlots = SortSlots(filteredSlots);
|
||||
|
||||
int siblingIndex = 0;
|
||||
|
||||
foreach (var slot in sortedSlots)
|
||||
{
|
||||
var model = slot.Model;
|
||||
string id = model.Id;
|
||||
|
||||
bool isRegisteredTodayMenu = model.ItemType == ItemType.Recipe && restaurantManagementStateSo.IsContainTodayMenu(id);
|
||||
bool matchCategory = MatchesCategory(model, _currenInventoryCategoryType);
|
||||
bool shouldShow = !isRegisteredTodayMenu && matchCategory;
|
||||
|
||||
slot.SetActive(shouldShow);
|
||||
|
||||
if (shouldShow && model.HasItem)
|
||||
{
|
||||
slot.transform.SetSiblingIndex(siblingIndex++);
|
||||
|
||||
if (firstValidSlot == null)
|
||||
{
|
||||
firstValidSlot = slot.gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
ClearObject(_slotParent);
|
||||
_viewModel.CreateInventoryItemSlot(_slotParent);
|
||||
|
||||
_firstSlot = firstValidSlot;
|
||||
}
|
||||
|
||||
private bool MatchesCategory(ItemViewModel model, InventoryCategoryType category)
|
||||
{
|
||||
switch (category)
|
||||
{
|
||||
case InventoryCategoryType.Food:
|
||||
if (model.ItemType != ItemType.Recipe) return false;
|
||||
|
||||
return DataManager.Instance.GetDataSo<RecipeDataSo>()
|
||||
.TryGetDataById(model.Id, out var foodRecipe) && foodRecipe.RecipeType == RecipeType.FoodRecipe;
|
||||
case InventoryCategoryType.Drink:
|
||||
if (model.ItemType != ItemType.Recipe) return false;
|
||||
|
||||
return DataManager.Instance.GetDataSo<RecipeDataSo>()
|
||||
.TryGetDataById(model.Id, out var drinkRecipe) &&
|
||||
drinkRecipe.RecipeType == RecipeType.DrinkRecipe;
|
||||
case InventoryCategoryType.Ingredient:
|
||||
return model.ItemType == ItemType.Ingredient;
|
||||
case InventoryCategoryType.Cookware:
|
||||
return DataManager.Instance.GetDataSo<CookwareDataSo>()
|
||||
.TryGetDataById(model.Id, out var cookwareData);
|
||||
case InventoryCategoryType.Special:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
EventBus.Register<InventoryChangedEvent>(this);
|
||||
EventBus.Register<TodayMenuAddedEvent>(this);
|
||||
EventBus.Register<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
public void UpdateView()
|
||||
{
|
||||
UpdateCategoryView(_currenInventoryCategoryType);
|
||||
_viewModel.UpdateCategoryView();
|
||||
}
|
||||
|
||||
public void UpdateCategoryView(InventoryCategoryType category) => _viewModel.UpdateCategoryViewByCategory(category);
|
||||
|
||||
private void Clear()
|
||||
private void ClearObject(Transform root)
|
||||
{
|
||||
foreach (Transform child in _slotParent)
|
||||
foreach (Transform child in root)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
@ -177,13 +57,7 @@ public void Invoke(TodayMenuRemovedEvent evt)
|
||||
|
||||
public void Invoke(InventoryChangedEvent evt)
|
||||
{
|
||||
foreach (var slot in _slotLookup.Values)
|
||||
{
|
||||
if (slot.Strategy is InventorySlotUiStrategy inventorySlotUiStrategy)
|
||||
{
|
||||
inventorySlotUiStrategy.OnInventoryChanged(slot);
|
||||
}
|
||||
}
|
||||
_viewModel.OnInventoryChanged();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,255 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
/// <summary>
|
||||
/// 인벤토리 UI의 ViewModel
|
||||
/// 기존 InventoryView의 상태와 로직을 MVVM 패턴으로 분리
|
||||
/// </summary>
|
||||
public class InventoryViewModel : SimpleViewModel, IEventHandler<InventoryChangedEvent>,
|
||||
IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
|
||||
{
|
||||
private InventoryService _inventoryService;
|
||||
|
||||
// Private fields for properties
|
||||
private InventoryCategoryType _currentCategory = InventoryCategoryType.Food;
|
||||
private InventorySortType _currentSortType = InventorySortType.None;
|
||||
private List<ItemViewModel> _allItems = new();
|
||||
private List<ItemViewModel> _visibleItems = new();
|
||||
private ItemViewModel _selectedItem;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 선택된 카테고리
|
||||
/// </summary>
|
||||
public InventoryCategoryType CurrentCategory
|
||||
{
|
||||
get => _currentCategory;
|
||||
set
|
||||
{
|
||||
if (SetField(ref _currentCategory, value))
|
||||
{
|
||||
UpdateVisibleItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 정렬 타입
|
||||
/// </summary>
|
||||
public InventorySortType CurrentSortType
|
||||
{
|
||||
get => _currentSortType;
|
||||
set
|
||||
{
|
||||
if (SetField(ref _currentSortType, value))
|
||||
{
|
||||
UpdateVisibleItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 인벤토리 아이템 목록
|
||||
/// </summary>
|
||||
public List<ItemViewModel> AllItems
|
||||
{
|
||||
get => _allItems;
|
||||
private set => SetField(ref _allItems, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 필터링된 보이는 아이템 목록
|
||||
/// </summary>
|
||||
public List<ItemViewModel> VisibleItems
|
||||
{
|
||||
get => _visibleItems;
|
||||
private set => SetField(ref _visibleItems, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 선택된 아이템
|
||||
/// </summary>
|
||||
public ItemViewModel SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetField(ref _selectedItem, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보이는 아이템들 중 실제 보유한 아이템이 있는지 확인
|
||||
/// </summary>
|
||||
public bool HasVisibleItems => VisibleItems.Any(item => item.HasItem);
|
||||
|
||||
/// <summary>
|
||||
/// 보이는 아이템들 중 실제 보유한 아이템 개수
|
||||
/// </summary>
|
||||
public int VisibleItemCount => VisibleItems.Count(item => item.HasItem);
|
||||
|
||||
/// <summary>
|
||||
/// 아이템 개수 표시 텍스트
|
||||
/// </summary>
|
||||
public string ItemCountText => $"아이템 수: {VisibleItemCount}";
|
||||
|
||||
/// <summary>
|
||||
/// 빈 목록 메시지 표시 여부
|
||||
/// </summary>
|
||||
public bool ShowEmptyMessage => !HasVisibleItems;
|
||||
|
||||
/// <summary>
|
||||
/// 첫 번째 유효한 아이템 (UI 포커스용)
|
||||
/// </summary>
|
||||
public ItemViewModel FirstValidItem => VisibleItems.FirstOrDefault(item => item.HasItem);
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
if (_inventoryService == null)
|
||||
_inventoryService = new InventoryService();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_inventoryService.Initialize();
|
||||
LoadInventoryData();
|
||||
RegisterEvents();
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
base.Cleanup();
|
||||
|
||||
UnregisterEvents();
|
||||
_inventoryService?.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이벤트 등록
|
||||
/// </summary>
|
||||
private void RegisterEvents()
|
||||
{
|
||||
EventBus.Register<InventoryChangedEvent>(this);
|
||||
EventBus.Register<TodayMenuAddedEvent>(this);
|
||||
EventBus.Register<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이벤트 등록 해제
|
||||
/// </summary>
|
||||
private void UnregisterEvents()
|
||||
{
|
||||
EventBus.Unregister<InventoryChangedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuAddedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 인벤토리 데이터 로드
|
||||
/// </summary>
|
||||
private void LoadInventoryData()
|
||||
{
|
||||
AllItems = _inventoryService.GetInventoryItems();
|
||||
UpdateVisibleItems();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리 설정 (UI에서 호출)
|
||||
/// </summary>
|
||||
/// <param name="category">새 카테고리</param>
|
||||
public void SetCategory(InventoryCategoryType category)
|
||||
{
|
||||
CurrentCategory = category;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 정렬 타입 설정 (UI에서 호출)
|
||||
/// </summary>
|
||||
/// <param name="sortType">새 정렬 타입</param>
|
||||
public void SetSortType(InventorySortType sortType)
|
||||
{
|
||||
CurrentSortType = sortType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보이는 아이템 목록 업데이트
|
||||
/// </summary>
|
||||
private void UpdateVisibleItems()
|
||||
{
|
||||
BeginUpdate(); // 배치 업데이트 시작
|
||||
|
||||
// 서비스에서 필터링된 아이템 가져오기
|
||||
var filteredItems = _inventoryService.FilterItems(CurrentCategory, CurrentSortType);
|
||||
|
||||
// 오늘의 메뉴에 등록된 아이템 제외
|
||||
var finalItems = _inventoryService.FilterOutTodayMenuItems(filteredItems);
|
||||
|
||||
VisibleItems = finalItems.ToList();
|
||||
|
||||
// 관련된 계산된 속성들 알림
|
||||
OnPropertyChanged(nameof(HasVisibleItems));
|
||||
OnPropertyChanged(nameof(VisibleItemCount));
|
||||
OnPropertyChanged(nameof(ItemCountText));
|
||||
OnPropertyChanged(nameof(ShowEmptyMessage));
|
||||
OnPropertyChanged(nameof(FirstValidItem));
|
||||
|
||||
EndUpdate(); // 배치 업데이트 종료
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이템 선택
|
||||
/// </summary>
|
||||
/// <param name="item">선택할 아이템</param>
|
||||
public void SelectItem(ItemViewModel item)
|
||||
{
|
||||
SelectedItem = item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특정 카테고리의 아이템 개수 가져오기
|
||||
/// </summary>
|
||||
/// <param name="category">카테고리</param>
|
||||
/// <returns>아이템 개수</returns>
|
||||
public int GetItemCountForCategory(InventoryCategoryType category)
|
||||
{
|
||||
return _inventoryService.GetItemCountByCategory(category);
|
||||
}
|
||||
|
||||
// Event Handlers
|
||||
|
||||
public void Invoke(InventoryChangedEvent evt)
|
||||
{
|
||||
LoadInventoryData();
|
||||
}
|
||||
|
||||
public void Invoke(TodayMenuAddedEvent evt)
|
||||
{
|
||||
UpdateVisibleItems();
|
||||
}
|
||||
|
||||
public void Invoke(TodayMenuRemovedEvent evt)
|
||||
{
|
||||
UpdateVisibleItems();
|
||||
}
|
||||
|
||||
// Unity Editor에서 테스트용 메서드들
|
||||
#if UNITY_EDITOR
|
||||
[ContextMenu("Test Category Change")]
|
||||
private void TestCategoryChange()
|
||||
{
|
||||
var nextCategory = (InventoryCategoryType)(((int)CurrentCategory + 1) % 5);
|
||||
SetCategory(nextCategory);
|
||||
}
|
||||
|
||||
[ContextMenu("Test Sort Change")]
|
||||
private void TestSortChange()
|
||||
{
|
||||
var nextSort = (InventorySortType)(((int)CurrentSortType + 1) % 5);
|
||||
SetSortType(nextSort);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 321a552f0b0773b4db1ab3dc95217719
|
@ -46,21 +46,32 @@ public override void Open(OpenPopupUiEvent evt)
|
||||
|
||||
InitializeViews();
|
||||
SetupTabs();
|
||||
|
||||
IsInitialized = true;
|
||||
}
|
||||
|
||||
protected override GameObject GetInitialSelected()
|
||||
{
|
||||
if (IsInitialized == false) return null;
|
||||
|
||||
// ViewModel의 현재 상태에 따라 초기 선택 UI 결정
|
||||
var inventoryInitialSelected = _inventoryView.GetInitialSelected();
|
||||
if (inventoryInitialSelected) return inventoryInitialSelected;
|
||||
if (inventoryInitialSelected)
|
||||
{
|
||||
return inventoryInitialSelected;
|
||||
}
|
||||
|
||||
var menuCategoryButton = _menuCategoryTabs.GetFirstInteractableButton();
|
||||
if (menuCategoryButton != null && menuCategoryButton.activeInHierarchy)
|
||||
{
|
||||
return menuCategoryButton;
|
||||
}
|
||||
|
||||
var cookwareCategoryButton = _cookwareCategoryTabs.GetFirstInteractableButton();
|
||||
if (cookwareCategoryButton != null && cookwareCategoryButton.activeInHierarchy)
|
||||
{
|
||||
return cookwareCategoryButton;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -101,7 +112,7 @@ private void SetupViewModelEvents()
|
||||
private void InitializeViews()
|
||||
{
|
||||
_checklistView.Initalize();
|
||||
_inventoryView.Initialize();
|
||||
_inventoryView.Initialize(_viewModel);
|
||||
_itemDetailView.Initialize();
|
||||
_todayMenuView.Initialize(_viewModel);
|
||||
_todayRestaurantStateView.Initialize();
|
||||
|
@ -6,9 +6,6 @@ namespace DDD
|
||||
{
|
||||
public class RestaurantManagementViewModel : SimpleViewModel, IEventHandler<TodayMenuRemovedEvent>
|
||||
{
|
||||
private List<ItemSlotUi> _foodSlots;
|
||||
private List<ItemSlotUi> _drinkSlots;
|
||||
|
||||
// View에서 구독할 이벤트들
|
||||
public System.Action OnBatchCompleted;
|
||||
public System.Action OnChecklistFailed;
|
||||
@ -183,8 +180,148 @@ public void MoveTab(int direction)
|
||||
|
||||
#endregion
|
||||
|
||||
#region InventoryView
|
||||
|
||||
private InventoryCategoryType _currenInventoryCategoryType = InventoryCategoryType.Food;
|
||||
|
||||
private readonly Dictionary<string, ItemSlotUi> _slotLookup = new();
|
||||
private InventorySortType _currentSortType = InventorySortType.None;
|
||||
private GameObject _firstSlot;
|
||||
private const string ItemSlotUiName = "ItemSlotUi_";
|
||||
public GameObject GetInitialSelectedByInventory() => _firstSlot;
|
||||
|
||||
public void CreateInventoryItemSlot(Transform parent)
|
||||
{
|
||||
var models = ItemViewModelFactory.CreateRestaurantManagementInventoryItem();
|
||||
|
||||
_slotLookup.Clear();
|
||||
foreach (var model in models)
|
||||
{
|
||||
var itemSlotUi = Instantiate(GetRestaurantManagementData().ItemSlotUiPrefab, parent);
|
||||
var slot = itemSlotUi.GetComponent<ItemSlotUi>();
|
||||
slot.Initialize(model, new InventorySlotUiStrategy());
|
||||
itemSlotUi.name = ItemSlotUiName + model.Id;
|
||||
|
||||
var interactor = itemSlotUi.GetComponent<ItemSlotInteractor>();
|
||||
if (model.ItemType == ItemType.Recipe)
|
||||
{
|
||||
interactor.Initialize(TodayMenuEventType.Add, new TodayMenuInteractorStrategy());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DataManager.Instance.GetDataSo<CookwareDataSo>().TryGetDataById(model.Id, out var cookwareData))
|
||||
{
|
||||
interactor.Initialize(TodayMenuEventType.Add, new TodayCookwareInteractorStrategy());
|
||||
}
|
||||
}
|
||||
|
||||
_slotLookup[model.Id] = slot;
|
||||
}
|
||||
|
||||
UpdateCategoryView();
|
||||
}
|
||||
|
||||
public void SetSortType(InventorySortType sortType)
|
||||
{
|
||||
_currentSortType = sortType;
|
||||
UpdateCategoryView();
|
||||
}
|
||||
|
||||
private IEnumerable<ItemSlotUi> SortSlots(IEnumerable<ItemSlotUi> slots)
|
||||
{
|
||||
return _currentSortType switch
|
||||
{
|
||||
InventorySortType.NameAscending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenBy(slot => slot.Model.DisplayName),
|
||||
InventorySortType.NameDescending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenByDescending(slot => slot.Model.DisplayName),
|
||||
InventorySortType.QuantityAscending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenBy(slot => slot.Model.Count),
|
||||
InventorySortType.QuantityDescending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenByDescending(slot => slot.Model.Count),
|
||||
InventorySortType.None => slots.OrderBy(slot => slot.Model.Id),
|
||||
_ => slots
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateCategoryView() => UpdateCategoryViewByCategory(_currenInventoryCategoryType);
|
||||
|
||||
public void UpdateCategoryViewByCategory(InventoryCategoryType category)
|
||||
{
|
||||
_currenInventoryCategoryType = category;
|
||||
GameObject firstValidSlot = null;
|
||||
|
||||
var filteredSlots = _slotLookup.Values;
|
||||
var sortedSlots = SortSlots(filteredSlots);
|
||||
|
||||
int siblingIndex = 0;
|
||||
|
||||
foreach (var slot in sortedSlots)
|
||||
{
|
||||
var model = slot.Model;
|
||||
string id = model.Id;
|
||||
|
||||
bool isRegisteredTodayMenu = model.ItemType == ItemType.Recipe && GetRestaurantManagementState().IsContainTodayMenu(id);
|
||||
bool matchCategory = MatchesCategory(model, _currenInventoryCategoryType);
|
||||
bool shouldShow = !isRegisteredTodayMenu && matchCategory;
|
||||
|
||||
slot.SetActive(shouldShow);
|
||||
|
||||
if (shouldShow && model.HasItem)
|
||||
{
|
||||
slot.transform.SetSiblingIndex(siblingIndex++);
|
||||
|
||||
if (firstValidSlot == null)
|
||||
{
|
||||
firstValidSlot = slot.gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_firstSlot = firstValidSlot;
|
||||
}
|
||||
|
||||
private bool MatchesCategory(ItemViewModel model, InventoryCategoryType category)
|
||||
{
|
||||
switch (category)
|
||||
{
|
||||
case InventoryCategoryType.Food:
|
||||
if (model.ItemType != ItemType.Recipe) return false;
|
||||
|
||||
return DataManager.Instance.GetDataSo<RecipeDataSo>()
|
||||
.TryGetDataById(model.Id, out var foodRecipe) && foodRecipe.RecipeType == RecipeType.FoodRecipe;
|
||||
case InventoryCategoryType.Drink:
|
||||
if (model.ItemType != ItemType.Recipe) return false;
|
||||
|
||||
return DataManager.Instance.GetDataSo<RecipeDataSo>()
|
||||
.TryGetDataById(model.Id, out var drinkRecipe) &&
|
||||
drinkRecipe.RecipeType == RecipeType.DrinkRecipe;
|
||||
case InventoryCategoryType.Ingredient:
|
||||
return model.ItemType == ItemType.Ingredient;
|
||||
case InventoryCategoryType.Cookware:
|
||||
return DataManager.Instance.GetDataSo<CookwareDataSo>()
|
||||
.TryGetDataById(model.Id, out var cookwareData);
|
||||
case InventoryCategoryType.Special:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnInventoryChanged()
|
||||
{
|
||||
foreach (var slot in _slotLookup.Values)
|
||||
{
|
||||
if (slot.Strategy is InventorySlotUiStrategy inventorySlotUiStrategy)
|
||||
{
|
||||
inventorySlotUiStrategy.OnInventoryChanged(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TodayMenuView
|
||||
|
||||
private List<ItemSlotUi> _foodSlots;
|
||||
private List<ItemSlotUi> _drinkSlots;
|
||||
|
||||
public void CreateFoodSlot(Transform parent)
|
||||
{
|
||||
var foodMaxCount = GetRestaurantManagementData().MaxFoodCount;
|
||||
|
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7196d7444953d844c9b0e893fb3e958a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,62 +0,0 @@
|
||||
namespace DDD
|
||||
{
|
||||
/// <summary>
|
||||
/// 서비스 계층의 기본 인터페이스
|
||||
/// MVVM 패턴에서 비즈니스 로직을 담당하는 서비스들의 공통 인터페이스
|
||||
/// </summary>
|
||||
public interface IService
|
||||
{
|
||||
/// <summary>
|
||||
/// 서비스 초기화
|
||||
/// </summary>
|
||||
void Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// 서비스 종료 시 리소스 정리
|
||||
/// </summary>
|
||||
void Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 데이터 서비스 인터페이스
|
||||
/// 데이터 CRUD 작업을 담당하는 서비스들의 기본 인터페이스
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">관리할 데이터 타입</typeparam>
|
||||
public interface IDataService<TData> : IService
|
||||
{
|
||||
/// <summary>
|
||||
/// 데이터 로드
|
||||
/// </summary>
|
||||
void LoadData();
|
||||
|
||||
/// <summary>
|
||||
/// 데이터 저장
|
||||
/// </summary>
|
||||
void SaveData();
|
||||
|
||||
/// <summary>
|
||||
/// 특정 ID의 데이터 가져오기
|
||||
/// </summary>
|
||||
/// <param name="id">데이터 ID</param>
|
||||
/// <returns>데이터 객체 또는 null</returns>
|
||||
TData GetData(string id);
|
||||
|
||||
/// <summary>
|
||||
/// 모든 데이터 가져오기
|
||||
/// </summary>
|
||||
/// <returns>모든 데이터 컬렉션</returns>
|
||||
System.Collections.Generic.IEnumerable<TData> GetAllData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI 서비스 인터페이스
|
||||
/// UI 관련 비즈니스 로직을 담당하는 서비스들의 기본 인터페이스
|
||||
/// </summary>
|
||||
public interface IUiService : IService
|
||||
{
|
||||
/// <summary>
|
||||
/// UI 상태 업데이트
|
||||
/// </summary>
|
||||
void UpdateUiState();
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f55f2be524632444aeebeb8b8965efc
|
Loading…
Reference in New Issue
Block a user