diff --git a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryAllDTO.swift b/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryAllDTO.swift index 0ac1e908..074cd7fe 100644 --- a/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryAllDTO.swift +++ b/MLS/Data/Data/Network/DTO/DictionaryDTO/DictionaryAllDTO.swift @@ -7,4 +7,3 @@ public struct DictionaryAllDTO: DictionaryDTOProtocol { public let bookmarkId: Int? public var id: Int { originalId } } - diff --git a/MLS/Data/Data/Network/DTO/SearchCountDTO/SearchCountDTO.swift b/MLS/Data/Data/Network/DTO/SearchCountDTO/SearchCountDTO.swift index bdbd49f0..885b5795 100644 --- a/MLS/Data/Data/Network/DTO/SearchCountDTO/SearchCountDTO.swift +++ b/MLS/Data/Data/Network/DTO/SearchCountDTO/SearchCountDTO.swift @@ -2,7 +2,7 @@ import DomainInterface public struct SearchCountDTO: Decodable { public let counts: Int? - + public func toDomain() -> SearchCountResponse { return SearchCountResponse(count: counts) } diff --git a/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift b/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift index abb885b3..e6831513 100644 --- a/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift @@ -11,13 +11,13 @@ public enum DictionaryListEndPoint { return .init(baseURL: base, path: "/api/v1/\(type)/counts", method: .GET, query: query) } // 전체 리스트 - public static func fetchAllList(keyword: String?, page: Int? = nil, size: Int? = nil) -> ResponsableEndPoint>{ - let query = DictionaryListQuery(keyword: keyword ?? "",page: page ?? 0, size: size ?? 20, sort: nil) + public static func fetchAllList(keyword: String?, page: Int? = nil, size: Int? = nil) -> ResponsableEndPoint> { + let query = DictionaryListQuery(keyword: keyword ?? "", page: page ?? 0, size: size ?? 20, sort: nil) return .init(baseURL: base, path: "/api/v1/search", method: .GET, query: query) } // 몬스터 리스트 public static func fetchMonsterList(keyword: String?, minLevel: Int?, maxLevel: Int?, page: Int, size: Int, sort: String?) -> ResponsableEndPoint> { - let query = DictionaryListQuery(keyword: keyword ?? "", page: page, size: size, sort: sort, minLevel: minLevel ?? 1, maxLevel: maxLevel ?? 200) + let query = DictionaryListQuery(keyword: keyword ?? "", page: page, size: size, sort: sort, minLevel: minLevel, maxLevel: maxLevel) return .init(baseURL: base, path: "/api/v1/monsters", method: .GET, query: query ) } @@ -34,8 +34,11 @@ public enum DictionaryListEndPoint { ) } // 아이템 리스트 - public static func fetchItemList(keyword: String? = nil, jobId: Int? = nil, minLevel: Int? = nil, maxLevel: Int? = nil, categoryIds: [Int]? = nil, page: Int? = nil, size: Int? = nil, sort: String? = nil) -> ResponsableEndPoint> { - let query = DictionaryListQuery(keyword: keyword, page: page ?? 0, size: size ?? 20, sort: sort, minLevel: minLevel, maxLevel: maxLevel, jobId: jobId) + public static func fetchItemList(keyword: String? = nil, jobId: [Int]? = nil, minLevel: Int? = nil, maxLevel: Int? = nil, categoryIds: [Int]? = nil, page: Int? = nil, size: Int? = nil, sort: String? = nil) -> ResponsableEndPoint> { + let joinedCategoryIds = categoryIds?.map(String.init).joined(separator: ",") + let joinedJobIds = jobId?.map(String.init).joined(separator: ",") + + let query = DictionaryListQuery(keyword: keyword, page: page ?? 0, size: size ?? 20, sort: sort, minLevel: minLevel, maxLevel: maxLevel, jobIds: joinedJobIds, categoryIds: joinedCategoryIds) return .init(baseURL: base, path: "/api/v1/items", method: .GET, query: query ) } diff --git a/MLS/Data/Data/Providers/Network/NetworkProviderImpl.swift b/MLS/Data/Data/Providers/Network/NetworkProviderImpl.swift index eece6493..959eb5d0 100644 --- a/MLS/Data/Data/Providers/Network/NetworkProviderImpl.swift +++ b/MLS/Data/Data/Providers/Network/NetworkProviderImpl.swift @@ -22,7 +22,7 @@ public final class NetworkProviderImpl: NetworkProvider { print("🚀 requestData: 요청 시작 - \(endPoint)") self?.sendRequest(endPoint: endPoint, interceptor: interceptor, completion: { result in - + switch result { case .success(let data): print("✅ requestData: 응답 수신") diff --git a/MLS/Data/Data/Repository/DictionaryListAPIRepositoryImpl.swift b/MLS/Data/Data/Repository/DictionaryListAPIRepositoryImpl.swift index 03217e4b..15c454a9 100644 --- a/MLS/Data/Data/Repository/DictionaryListAPIRepositoryImpl.swift +++ b/MLS/Data/Data/Repository/DictionaryListAPIRepositoryImpl.swift @@ -27,7 +27,7 @@ public final class DictionaryListAPIRepositoryImpl: DictionaryListAPIRepository } // MARK: - 몬스터 리스트 public func fetchMonsterList(keyword: String?, minLevel: Int?, maxLevel: Int?, page: Int, size: Int, sort: String?) -> Observable { - let endPoint = DictionaryListEndPoint.fetchMonsterList(keyword: keyword, minLevel: minLevel ?? 1, maxLevel: maxLevel ?? 200, page: page, size: size, sort: sort ?? "ASC") + let endPoint = DictionaryListEndPoint.fetchMonsterList(keyword: keyword, minLevel: minLevel, maxLevel: maxLevel, page: page, size: size, sort: sort ?? "ASC") return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) .map { $0.toDomain() } } @@ -44,7 +44,7 @@ public final class DictionaryListAPIRepositoryImpl: DictionaryListAPIRepository .map { $0.toDomain() } } // MARK: - Item 리스트 - public func fetchItemList(keyword: String?, jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, page: Int?, size: Int?, sort: String?) -> Observable { + public func fetchItemList(keyword: String?, jobId: [Int]?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, page: Int?, size: Int?, sort: String?) -> Observable { let endPoint = DictionaryListEndPoint.fetchItemList(keyword: keyword, jobId: jobId, minLevel: minLevel, maxLevel: maxLevel, categoryIds: categoryIds, page: page, size: size, sort: sort) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) .map { $0.toDomain() } diff --git a/MLS/Data/Data/Repository/UserDefaultsRepositoryImpl.swift b/MLS/Data/Data/Repository/UserDefaultsRepositoryImpl.swift index ee92e589..39d6a334 100644 --- a/MLS/Data/Data/Repository/UserDefaultsRepositoryImpl.swift +++ b/MLS/Data/Data/Repository/UserDefaultsRepositoryImpl.swift @@ -16,21 +16,21 @@ public final class UserDefaultsRepositoryImpl: UserDefaultsRepository { return Disposables.create() } } - + public func addRecentSearch(keyword: String) -> Completable { return Completable.create { completable in var current = UserDefaults.standard.stringArray(forKey: self.key) ?? [] - + // 중복 제거 current.removeAll(where: { $0 == keyword}) current.insert(keyword, at: 0) - + UserDefaults.standard.set(current, forKey: self.key) completable(.completed) return Disposables.create() } } - + public func removeRecentSearch(keyword: String) -> Completable { return Completable.create { completable in var current = UserDefaults.standard.stringArray(forKey: self.key) ?? [] diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryAllListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryAllListUseCaseImpl.swift index 37b5b4a1..b146cee6 100644 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryAllListUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryAllListUseCaseImpl.swift @@ -3,13 +3,13 @@ import DomainInterface import RxSwift public final class FetchDictionaryAllListUseCaseImpl: FetchDictionaryAllListUseCase { - + private let repository: DictionaryListAPIRepository - + public init(repository: DictionaryListAPIRepository) { self.repository = repository } - + public func execute(keyword: String?, page: Int?) -> Observable { return repository.fetchAllList(keyword: keyword, page: page) } diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryItemListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryItemListUseCaseImpl.swift index a190cbfe..c9395f7b 100644 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryItemListUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionaryItemListUseCaseImpl.swift @@ -10,7 +10,7 @@ public final class FetchDictionaryItemListUseCaseImpl: FetchDictionaryItemListUs self.repository = repository } - public func execute(keyword: String?, jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, page: Int?, size: Int?, sort: String?) -> Observable { + public func execute(keyword: String?, jobId: [Int]?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, page: Int?, size: Int?, sort: String?) -> Observable { return repository.fetchItemList(keyword: keyword, jobId: jobId, minLevel: minLevel, maxLevel: maxLevel, categoryIds: categoryIds, page: page, size: size, sort: sort) } } diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionarySearchListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionarySearchListUseCaseImpl.swift index 6c5cfaa2..69e0fcb4 100644 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionarySearchListUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/FetchDictionarySearchListUseCaseImpl.swift @@ -4,11 +4,11 @@ import RxSwift public final class FetchDictionarySearchListUseCaseImpl: FetchDictionarySearchListUseCase { private let repository: DictionaryListAPIRepository - + public init(repository: DictionaryListAPIRepository) { self.repository = repository } - + public func execute(keyword: String) -> Observable { return repository.fetchAllList(keyword: keyword, page: nil) } diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift new file mode 100644 index 00000000..b2a9bf37 --- /dev/null +++ b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift @@ -0,0 +1,57 @@ +import DomainInterface + +public final class ParseItemFilterResultUseCaseImpl: ParseItemFilterResultUseCase { + + private let jobIdMap: [String: Int] = [ + "전사": 100, "마법사": 200, "도적": 400, "궁수": 300 + ] + private let categoryIdMap: [String: Int] = [ + "한손검": 7, "한손도끼": 8, "한손둔기": 9, "단검": 10, "완드": 12, + "스태프": 13, "두손검": 14, "두손도끼": 15, "두손둔기": 16, "창": 17, + "폴암": 18, "활": 19, "석궁": 20, "아대": 21, "모자": 24, "상의": 25, + "하의": 26, "전신갑옷": 27, "신발": 28, "장갑": 29, "방패": 31, "망토": 30, + "얼굴장식": 32, "눈장식": 33, "귀고리": 34, "반지": 35, "펜던트": 36, + "벨트": 37, "어깨장식": 38, "화살": 81, "표창": 83, + "한손검주문서": 50, "한손도끼주문서": 51, "한손둔기주문서": 52, + "단검주문서": 53, "완드주문서": 55, "스태프주문서": 56, "두손검주문서": 57, + "두손도끼주문서": 58, "두손둔기주문서": 59, "창주문서": 60, "폴암주문서": 61, + "활주문서": 62, "석궁주문서": 63, "아대주문서": 64, "투구주문서": 67, "상의주문서": 68, "하의주문서": 69, "전신갑옷주문서": 70, "신발주문서": 71, "장갑주문서": 72, "망토주문서": 73, "방패주문서": 74, + "귀장식주문서": 78, "펫장비주문서": 75, "연성서주문서": 76, "귀환주문서": 77, + "마스터리북": 42, "스킬북": 43, "소비": 44, "설치": 45, "이동수단": 46 + ] + + public init() {} + + public func execute(results: [(String, String)]) -> ItemFilterCriteria { + let initialCriteria = (jobIds: [Int](), startLevel: Int?(nil), endLevel: Int?(nil), categoryIds: [Int]()) + + let finalCriteria = results.reduce(into: initialCriteria) { criteria, result in + let (key, value) = result + switch key { + case "직업": + if let id = jobIdMap[value] { + criteria.jobIds.append(id) + } + case "레벨": + let levelText = value.replacingOccurrences(of: "레벨", with: "").trimmingCharacters(in: .whitespaces) + let parts = levelText.split(separator: "~").map { $0.trimmingCharacters(in: .whitespaces) } + if let low = Int(parts.first ?? ""), let high = Int(parts.last ?? "") { + criteria.startLevel = low + criteria.endLevel = high + } + case "무기", "발사체", "방어구", "장신구", "기타아이템": + if let id = categoryIdMap[value] { + criteria.categoryIds.append(id) + } + case "무기주문서", "방어구주문서", "기타주문서": + if let id = categoryIdMap[value + "주문서"] { + criteria.categoryIds.append(id) + } + default: + break + } + } + + return ItemFilterCriteria(jobIds: finalCriteria.jobIds, startLevel: finalCriteria.startLevel, endLevel: finalCriteria.endLevel, categoryIds: finalCriteria.categoryIds) + } +} diff --git a/MLS/Domain/Domain/UseCaseImpl/DictionaryListCount/FetchDictionaryListCountUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryListCount/FetchDictionaryListCountUseCaseImpl.swift index 75743ab4..eb58efa8 100644 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryListCount/FetchDictionaryListCountUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/DictionaryListCount/FetchDictionaryListCountUseCaseImpl.swift @@ -4,11 +4,11 @@ import RxSwift public final class FetchDictionaryListCountUseCaseImpl: FetchDictionaryListCountUseCase { private let repository: DictionaryListAPIRepository - + public init(repository: DictionaryListAPIRepository) { self.repository = repository } - + public func execute(type: String, keyword: String?) -> Observable { return repository.fetchSearchListCount(type: type, keyword: keyword) } diff --git a/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchAddUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchAddUseCaseImpl.swift index 566d981c..a56d8544 100644 --- a/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchAddUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchAddUseCaseImpl.swift @@ -5,13 +5,13 @@ import RxSwift public class RecentSearchAddUseCaseImpl: RecentSearchAddUseCase { var repository: UserDefaultsRepository - + public init(repository: UserDefaultsRepository) { self.repository = repository } - + public func add(keyword: String) -> Completable { return repository.addRecentSearch(keyword: keyword) } - + } diff --git a/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchFetchUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchFetchUseCaseImpl.swift index 87afda9e..e4e440d7 100644 --- a/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchFetchUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/RecentSearch/RecentSearchFetchUseCaseImpl.swift @@ -4,11 +4,11 @@ import RxSwift public final class RecentSearchFetchUseCaseImpl: RecentSearchFetchUseCase { private let repository: UserDefaultsRepository - + public init(repository: UserDefaultsRepository) { self.repository = repository } - + public func fetch() -> Observable<[String]> { return repository.fetchRecentSearch() } diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryListQuery.swift b/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryListQuery.swift index f3c42ab0..26d19bfd 100644 --- a/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryListQuery.swift +++ b/MLS/Domain/DomainInterface/Entity/DictionaryList/DictionaryListQuery.swift @@ -9,17 +9,17 @@ public struct DictionaryListQuery: Encodable { public var maxLevel: Int? // item일 경우 - public var jobId: Int? - public var categoryIds: [Int]? + public var jobIds: String? + public var categoryIds: String? - public init(keyword: String? = nil, page: Int, size: Int, sort: String?, minLevel: Int? = nil, maxLevel: Int? = nil, jobId: Int? = nil, categoryIds: [Int]? = nil) { + public init(keyword: String? = nil, page: Int, size: Int, sort: String?, minLevel: Int? = nil, maxLevel: Int? = nil, jobIds: String? = nil, categoryIds: String? = nil) { self.keyword = keyword self.page = page self.size = size self.sort = sort ?? "ASC" self.minLevel = minLevel self.maxLevel = maxLevel - self.jobId = jobId + self.jobIds = jobIds self.categoryIds = categoryIds } } diff --git a/MLS/Domain/DomainInterface/Entity/DictionaryList/ItemFilterCriteria.swift b/MLS/Domain/DomainInterface/Entity/DictionaryList/ItemFilterCriteria.swift new file mode 100644 index 00000000..8fe61cd6 --- /dev/null +++ b/MLS/Domain/DomainInterface/Entity/DictionaryList/ItemFilterCriteria.swift @@ -0,0 +1,13 @@ +public struct ItemFilterCriteria { + public let jobIds: [Int] + public let startLevel: Int? + public let endLevel: Int? + public let categoryIds: [Int] + + public init(jobIds: [Int], startLevel: Int?, endLevel: Int?, categoryIds: [Int]) { + self.jobIds = jobIds + self.startLevel = startLevel + self.endLevel = endLevel + self.categoryIds = categoryIds + } +} diff --git a/MLS/Domain/DomainInterface/Entity/SearchCount/SearchCountResponse.swift b/MLS/Domain/DomainInterface/Entity/SearchCount/SearchCountResponse.swift index 5401eeaa..ef99985a 100644 --- a/MLS/Domain/DomainInterface/Entity/SearchCount/SearchCountResponse.swift +++ b/MLS/Domain/DomainInterface/Entity/SearchCount/SearchCountResponse.swift @@ -1,6 +1,6 @@ public struct SearchCountResponse: Decodable { public let count: Int? - + public init(count: Int?) { self.count = count } diff --git a/MLS/Domain/DomainInterface/Repository/DictionaryListAPIRepository.swift b/MLS/Domain/DomainInterface/Repository/DictionaryListAPIRepository.swift index b854a002..5ea6acdb 100644 --- a/MLS/Domain/DomainInterface/Repository/DictionaryListAPIRepository.swift +++ b/MLS/Domain/DomainInterface/Repository/DictionaryListAPIRepository.swift @@ -10,7 +10,7 @@ public protocol DictionaryListAPIRepository { // Quest func fetchQuestList(keyword: String, page: Int, size: Int, sort: String?) -> Observable // Item - func fetchItemList(keyword: String?, jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, page: Int?, size: Int?, sort: String?) -> Observable + func fetchItemList(keyword: String?, jobId: [Int]?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, page: Int?, size: Int?, sort: String?) -> Observable // Map func fetchMapList(keyword: String, page: Int, size: Int, sort: String?) -> Observable // 검색 diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryItemListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryItemListUseCase.swift index d84d3693..16ecffba 100644 --- a/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryItemListUseCase.swift +++ b/MLS/Domain/DomainInterface/UseCase/DictionaryList/FetchDictionaryItemListUseCase.swift @@ -2,5 +2,5 @@ import RxSwift public protocol FetchDictionaryItemListUseCase { /// 어떤 타입인지에 따라서 쿼리가 결정됨 -> 타입 마다 쿼리가 댜름. - func execute(keyword: String?, jobId: Int?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, page: Int?, size: Int?, sort: String?) -> Observable + func execute(keyword: String?, jobId: [Int]?, minLevel: Int?, maxLevel: Int?, categoryIds: [Int]?, page: Int?, size: Int?, sort: String?) -> Observable } diff --git a/MLS/Domain/DomainInterface/UseCase/DictionaryList/ParseItemFilterUseCase.swift b/MLS/Domain/DomainInterface/UseCase/DictionaryList/ParseItemFilterUseCase.swift new file mode 100644 index 00000000..13a68723 --- /dev/null +++ b/MLS/Domain/DomainInterface/UseCase/DictionaryList/ParseItemFilterUseCase.swift @@ -0,0 +1,3 @@ +public protocol ParseItemFilterResultUseCase { + func execute(results: [(String, String)]) -> ItemFilterCriteria +} diff --git a/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift b/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift index 4709b86f..d2aa8973 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift +++ b/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift @@ -148,11 +148,21 @@ public extension BaseListView { return button } - func selectFilter(selectedType: SortType) { + func selectSort(selectedType: SortType) { sortButton.setAttributedTitle(.makeStyledString(font: .b_s_r, text: selectedType.rawValue, color: .primary700), for: .normal) sortButton.tintColor = .primary700 } + func selectFilter() { + filterButton.setAttributedTitle(.makeStyledString(font: .b_s_r, text: "필터", color: .primary700), for: .normal) + filterButton.tintColor = .primary700 + } + + func resetFilter() { + filterButton.setAttributedTitle(.makeStyledString(font: .b_s_r, text: "필터"), for: .normal) + filterButton.tintColor = .black + } + func checkEmptyData(isEmpty: Bool) { emptyView.isHidden = !isEmpty filterStackView.isHidden = isEmpty diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift index 1fdd4492..25ae0aa1 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift @@ -135,15 +135,15 @@ extension BookmarkListViewController { owner.selectedSortIndex = index let selectedFilter = reactor.currentState.type.bookmarkSortedFilter[index] reactor.action.onNext(.sortOptionSelected(selectedFilter)) - owner.mainView.selectFilter(selectedType: selectedFilter) + owner.mainView.selectSort(selectedType: selectedFilter) } owner.tabBarController?.presentModal(viewController) case .filter(let type): switch type { case .item: break - //let viewController = owner.itemFilterFactory.make() - //owner.present(viewController, animated: true) + // let viewController = owner.itemFilterFactory.make() + // owner.present(viewController, animated: true) case .monster: let viewController = owner.monsterFilterFactory.make(startLevel: reactor.currentState.startLevel ?? 1, endLevel: reactor.currentState.endLevel ?? 200) { startLevel, endLevel in diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift index 9f9073b4..beb3a61d 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift @@ -28,35 +28,35 @@ class DictionaryDetailBaseViewController: BaseViewController { // MARK: - Components public var mainView = DictionaryDetailBaseView() - + // 타입설정 public var type: DictionaryItemType - + public init(type: DictionaryItemType) { self.type = type mainView.titleLabel.attributedText = .makeStyledString(font: .sub_m_b, text: type.detailTitle) super.init() isBottomTabbarHidden = true } - + @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func viewDidLoad() { super.viewDidLoad() - + addViews() setupConstraints() bindActions() // 액션 바인딩 mainView.scrollView.delegate = self setupMenu(type.detailTypes) } - + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - + // 처음 진입 및 뷰가 추가 되었는지 확인 if !didSelectInitialTab { didSelectMenuTab(index: 0) @@ -70,7 +70,7 @@ private extension DictionaryDetailBaseViewController { func addViews() { view.addSubview(mainView) } - + func setupConstraints() { mainView.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide) @@ -84,7 +84,7 @@ extension DictionaryDetailBaseViewController: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { // 탭바의 frame을 self.view 기준으로 변환 let tabBarY = mainView.tabBarStackView.convert(mainView.tabBarStackView.bounds, to: view) - + if tabBarY.origin.y <= view.safeAreaInsets.top + DictionaryDetailBaseView.Constant.buttonSize + DictionaryDetailBaseView.Constant.horizontalInset - DictionaryDetailBaseView.Constant.tabBarStackViewInset.top { // safearea + 헤더뷰 + 헤더뷰와의 간격 16 = 122 mainView.tabBarStickyStackView.isHidden = false mainView.stickyTabBarDividerView.isHidden = false @@ -92,9 +92,9 @@ extension DictionaryDetailBaseViewController: UIScrollViewDelegate { mainView.tabBarStickyStackView.isHidden = true mainView.stickyTabBarDividerView.isHidden = true } - + let nameY = mainView.nameLabel.convert(mainView.nameLabel.bounds, to: view) - + if nameY.origin.y <= view.safeAreaInsets.top + DictionaryDetailBaseView.Constant.buttonSize + DictionaryDetailBaseView.Constant.horizontalInset { // 메랜에서 이름이 가장 긴 몬스터의 경우 '다크 주니어 예티와 페페'로 알고 있는데, 따로 텍스트 길이에 대한 제약사항을 안줘도 다크 주니어 예티와 페페가 잘 표시가 됨. 제약사항 필요한가? mainView.titleLabel.attributedText = .makeStyledString(font: .sub_m_b, text: mainView.nameLabel.text) @@ -115,7 +115,7 @@ extension DictionaryDetailBaseViewController { let name: String let subText: String? // 없는 경우도 있는듯 } - + func inject(input: Input) { // Load image if URL exists if let imageUrlString = input.imageUrl { @@ -123,7 +123,7 @@ extension DictionaryDetailBaseViewController { guard let self = self, let image = image else { return } self.mainView.imageView.image = image } - + } else { mainView.imageView.image = nil // Clear image if no URL } @@ -131,33 +131,33 @@ extension DictionaryDetailBaseViewController { mainView.nameLabel.attributedText = .makeStyledString(font: .sub_l_m, text: input.name, color: .textColor) mainView.subTextLabel.attributedText = .makeStyledString(font: .b_s_r, text: input.subText, color: .neutral500) } - + func makeTagsRow(_ tags: Effectiveness) { let maxWidth = UIScreen.main.bounds.width - DictionaryDetailBaseView.Constant.horizontalInset // 좌우 여백 고려 (16 * 2) let tagSpacing: CGFloat = DictionaryDetailBaseView.Constant.tagVerticalSpacing - + var tagsRowStackView = mainView.createHorizontalStackView() mainView.tagsVerticalStackView.addArrangedSubview(tagsRowStackView) - + var currentRowWidth: CGFloat = 0 for (element, value) in tags.nonNilElements() { - + let badge = Badge(style: .element("\(element.rawValue) \(value)")) let fittingSize = badge.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) let badgeWidth = fittingSize.width - + if currentRowWidth + badgeWidth + tagSpacing > maxWidth { tagsRowStackView = mainView.createHorizontalStackView() mainView.tagsVerticalStackView.addArrangedSubview(tagsRowStackView) currentRowWidth = 0 } - + tagsRowStackView.addArrangedSubview(badge) currentRowWidth += badgeWidth + tagSpacing mainView.setBadgeConstraints(badge, width: badgeWidth) } } - + // 탭바 메뉴 버튼 구성하기(스티키 쪽도 똑같이 구현) func setupMenu(_ menus: [DetailType]) { var firstIndexButton: UIButton? // 처음 화면 나올때 첫번째 버튼 클릭되게 하기 위해 첫번째 버튼 저장할 변수 @@ -169,16 +169,16 @@ extension DictionaryDetailBaseViewController { self?.menuTabTapped(button) } .disposed(by: disposeBag) - + let stickyButton = mainView.createMenuButton(title: menu.description, tag: index) stickyButton.rx.tap.bind { [weak self] _ in self?.menuTabTapped(stickyButton) } .disposed(by: disposeBag) - + mainView.tabBarStackView.addArrangedSubview(button) mainView.tabBarStickyStackView.addArrangedSubview(stickyButton) // 스티키 역할을 할 스택뷰에다가도 똑같은 버튼 추가 - + if index == 0 { firstIndexButton = button firstStickyIndexButton = button @@ -191,26 +191,26 @@ extension DictionaryDetailBaseViewController { menuTabTapped(firstStickyIndexButton) } } - + private func menuTabTapped(_ sender: UIButton) { let selectedTag = sender.tag - + updateButtonStates(in: mainView.tabBarStackView, selectedTag: selectedTag) // 스티키 탭 버튼 상태 변경 updateButtonStates(in: mainView.tabBarStickyStackView, selectedTag: selectedTag) - + // 자식 디테일 뷰컨에서 오버라이드 해서 사용하기? didSelectMenuTab(index: sender.tag) } - + // 버튼 상태 변경 함수 private func updateButtonStates(in stackView: UIStackView, selectedTag: Int) { for (index, subview) in stackView.arrangedSubviews.enumerated() { guard let button = subview as? UIButton else { continue } let title = button.titleLabel?.text ?? "" - + let underline = button.subviews.first { $0.tag == DictionaryDetailBaseView.Constant.underTag } - + if index == selectedTag { button.setAttributedTitle(.makeStyledString(font: .sub_m_b, text: title, color: .black), for: .normal) underline?.isHidden = false @@ -220,16 +220,16 @@ extension DictionaryDetailBaseViewController { } } } - + // 북마크 버튼 클릭 시 func updateBookmarkButton(isBookmarked: Bool) { // TODO: 북마크 버튼 누르면 이벤트 발생 } - + func didSelectMenuTab(index: Int) { // 인덱스 유효성 검사 guard index < contentViews.count else { return } - + // 현재 뷰가 같다면 변경 안함 if currentTabIndex == index { return } // 각 탭에 맞는 뷰 설정 @@ -244,7 +244,7 @@ private extension DictionaryDetailBaseViewController { bindBackButton() bindBookmarkButton() } - + func bindBackButton() { mainView.backButton.rx.tap .bind { [weak self] in @@ -252,7 +252,7 @@ private extension DictionaryDetailBaseViewController { } .disposed(by: disposeBag) } - + // 이부분은 왜 inject로 넣어야 하나?? func bindBookmarkButton() { mainView.bookmarkButton.rx.tap diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift index 629a7c8b..ffd6edfd 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift @@ -13,10 +13,8 @@ final class ItemDictionaryDetailViewController: DictionaryDetailBaseViewControll private let detailInfoView = DetailStackInfoView(type: .item) private let monsterCardView = DetailStackCardView() private let sortedFactory: SortedBottomSheetFactory = SortedBottomSheetFactoryImpl() - -} - +} // MARK: - Populate Data private extension ItemDictionaryDetailViewController { diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift index 7d3108a8..c6258d41 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift @@ -20,7 +20,7 @@ class MonsterDictionaryDetailViewController: DictionaryDetailBaseViewController, private var appearMapView = DetailStackCardView() private var dropItemView = DetailStackCardView() private let sortedFactory: SortedBottomSheetFactory = SortedBottomSheetFactoryImpl() - + } // MARK: - Populate Data diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift index cf196293..85810c73 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift @@ -11,7 +11,7 @@ public final class DictionaryListFactoryImpl: DictionaryMainListFactory { private let dictionaryQuestListItemUseCase: FetchDictionaryQuestListUseCase private let dictionaryNpcListItemUseCase: FetchDictionaryNpcListUseCase private let dictionaryListItemUseCase: FetchDictionaryMonsterListUseCase - + private let parseItemFilterResultUseCase: ParseItemFilterResultUseCase private let setBookmarkUseCase: SetBookmarkUseCase private let itemFilterFactory: ItemFilterBottomSheetFactory @@ -29,6 +29,7 @@ public final class DictionaryListFactoryImpl: DictionaryMainListFactory { dictionaryNpcListItemUseCase: FetchDictionaryNpcListUseCase, dictionaryListItemUseCase: FetchDictionaryMonsterListUseCase, setBookmarkUseCase: SetBookmarkUseCase, + parseItemFilterResultUseCase: ParseItemFilterResultUseCase, itemFilterFactory: ItemFilterBottomSheetFactory, monsterFilterFactory: MonsterFilterBottomSheetFactory, sortedFactory: SortedBottomSheetFactory, @@ -43,6 +44,7 @@ public final class DictionaryListFactoryImpl: DictionaryMainListFactory { self.dictionaryNpcListItemUseCase = dictionaryNpcListItemUseCase self.dictionaryListItemUseCase = dictionaryListItemUseCase self.setBookmarkUseCase = setBookmarkUseCase + self.parseItemFilterResultUseCase = parseItemFilterResultUseCase self.itemFilterFactory = itemFilterFactory self.monsterFilterFactory = monsterFilterFactory self.sortedFactory = sortedFactory @@ -61,7 +63,8 @@ public final class DictionaryListFactoryImpl: DictionaryMainListFactory { dictionaryQuestListUseCase: dictionaryQuestListItemUseCase, dictionaryNpcListUseCase: dictionaryNpcListItemUseCase, dictionaryListUseCase: dictionaryListItemUseCase, - setBookmarkUseCase: setBookmarkUseCase + setBookmarkUseCase: setBookmarkUseCase, + parseItemFilterResultUseCase: parseItemFilterResultUseCase ) let viewController = DictionaryListViewController(reactor: reactor, itemFilterFactory: itemFilterFactory, monsterFilterFactory: monsterFilterFactory, sortedFactory: sortedFactory, bookmarkModalFactory: bookmarkModalFactory, detailFactory: detailFactory) if listType == .search { diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift index 2ab9d70a..4fa5f65b 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift @@ -19,11 +19,11 @@ open class DictionaryListReactor: Reactor { case filterButtonTapped case sortOptionSelected(SortType) // 정렬 옵션 선택 시 액션 case filterOptionSelected(startLevel: Int, endLevel: Int) // 필터 옵션 선택 시 액션 + case itemFilterOptionSelected([(String, String)]) case setCurrentPage case fetchList // data 불러오기 case fetchListFilter // 필터링된 data 불러오기 case undoLastDeletedBookmark - // case itemFilterOptionSelected([String]) // 아이템 필터 옵션 선택 시 액션 } // MARK: - Mutation @@ -34,11 +34,13 @@ open class DictionaryListReactor: Reactor { case showSortFilter case showFilter case setSort(String) - case setFilter(start: Int, end: Int) + case setFilter(start: Int?, end: Int?) case setCurrentPage case initPage case setLoginState(Bool) case setLastDeletedBookmark(DictionaryMainItemResponse?) + case setJobId([Int]) // jobId 설정 + case setCategoryId([Int]) } // MARK: - State @@ -49,13 +51,13 @@ open class DictionaryListReactor: Reactor { // 필터 조건 public var keyword: String? - public var jobId: Int? + public var jobId: [Int]? public var minLevel: Int? public var maxLevel: Int? public var categoryIds: [Int]? public var sort: String? - public var startLevel: Int? - public var endLevel: Int? + public var startLevel: Int? = 0 + public var endLevel: Int? = 200 public var currentPage = 0 public var totalCounts = 0 @@ -75,6 +77,7 @@ open class DictionaryListReactor: Reactor { private let dictionaryNpcListUseCase: FetchDictionaryNpcListUseCase private let dictionaryListUseCase: FetchDictionaryMonsterListUseCase private let setBookmarkUseCase: SetBookmarkUseCase + private let parseItemFilterResultUseCase: ParseItemFilterResultUseCase private let disposeBag = DisposeBag() @@ -89,7 +92,8 @@ open class DictionaryListReactor: Reactor { dictionaryQuestListUseCase: FetchDictionaryQuestListUseCase, dictionaryNpcListUseCase: FetchDictionaryNpcListUseCase, dictionaryListUseCase: FetchDictionaryMonsterListUseCase, - setBookmarkUseCase: SetBookmarkUseCase + setBookmarkUseCase: SetBookmarkUseCase, + parseItemFilterResultUseCase: ParseItemFilterResultUseCase ) { self.initialState = State(route: .none, type: type, keyword: keyword, isLogin: false) self.checkLoginUseCase = checkLoginUseCase @@ -100,6 +104,7 @@ open class DictionaryListReactor: Reactor { self.dictionaryNpcListUseCase = dictionaryNpcListUseCase self.dictionaryListUseCase = dictionaryListUseCase self.setBookmarkUseCase = setBookmarkUseCase + self.parseItemFilterResultUseCase = parseItemFilterResultUseCase } // MARK: - Mutate @@ -108,7 +113,7 @@ open class DictionaryListReactor: Reactor { case .viewDidLoad: // 로그인 체크 + 초기 데이터 fetch return checkLoginUseCase.execute() - .flatMap { [weak self] isLoggedIn -> Observable in + .flatMap { [weak self] _ -> Observable in guard let self = self else { return .empty() } // if !isLoggedIn { @@ -243,11 +248,27 @@ open class DictionaryListReactor: Reactor { .just(.setLastDeletedBookmark(nil)) ]) ) + case .itemFilterOptionSelected(let results): + let criteria = parseItemFilterResultUseCase.execute(results: results) + return .concat([ + .just(.setJobId(criteria.jobIds)), + .just(.setFilter(start: criteria.startLevel, end: criteria.endLevel)), + .just(.setCategoryId(criteria.categoryIds)), + .just(.initPage) + ]) + .concat(Observable.deferred { [weak self] in + guard let self = self else { return .empty() } + return self.fetchListFilter( + sort: self.currentState.sort, + startLevel: self.currentState.startLevel, + endLevel: self.currentState.endLevel + ) + }) } } // MARK: - Fetch List (필터 적용) - private func fetchListFilter(sort: String?, startLevel: Int = 1, endLevel: Int = 200) -> Observable { + private func fetchListFilter(sort: String?, startLevel: Int?, endLevel: Int?) -> Observable { switch currentState.type { case .monster: return dictionaryListUseCase @@ -258,8 +279,8 @@ open class DictionaryListReactor: Reactor { .execute( keyword: currentState.keyword, jobId: currentState.jobId, - minLevel: currentState.minLevel, - maxLevel: currentState.maxLevel, + minLevel: currentState.startLevel, + maxLevel: currentState.endLevel, categoryIds: currentState.categoryIds, page: currentState.currentPage, size: 20, @@ -341,6 +362,10 @@ open class DictionaryListReactor: Reactor { newState.lastDeletedBookmark = item case .setLoginState(let isLogin): newState.isLogin = isLogin + case .setJobId(let id): + newState.jobId = id + case .setCategoryId(let id): + newState.categoryIds = id } return newState } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift index 1162f051..00e1d9ea 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift @@ -49,7 +49,7 @@ public final class DictionaryListViewController: BaseViewController, View { addViews() setupConstraints() configureUI() - + } } @@ -103,13 +103,13 @@ extension DictionaryListViewController { } func bindViewState(reactor: Reactor) { - + reactor.state .map { $0.totalCounts } .distinctUntilChanged() .bind(to: itemCountRelay) .disposed(by: disposeBag) - + reactor.state.map(\.listItems) .distinctUntilChanged() .observe(on: MainScheduler.instance) @@ -117,7 +117,9 @@ extension DictionaryListViewController { self?.mainView.listCollectionView.reloadData() self?.mainView.emptyView.isHidden = !item.isEmpty self?.mainView.listCollectionView.isHidden = item.isEmpty - self?.mainView.isUserInteractionEnabled = !item.isEmpty + // 보여줄 item이 없을 경우, 터치를 막는데 왜 막는건지? + // 몬스터나 아이템 탭에서 필터링을 하다가 item이 없을 경우, 필터 버튼도 터치가 안되서 계속 item 없음 + // self?.mainView.isUserInteractionEnabled = !item.isEmpty }) .disposed(by: disposeBag) @@ -137,20 +139,26 @@ extension DictionaryListViewController { owner.selectedSortIndex = index let selectedFilter = reactor.currentState.type.bookmarkSortedFilter[index] reactor.action.onNext(.sortOptionSelected(selectedFilter)) - owner.mainView.selectFilter(selectedType: selectedFilter) + owner.mainView.selectSort(selectedType: selectedFilter) reactor.action.onNext(.fetchListFilter) } owner.tabBarController?.presentModal(viewController) case .filter(let type): switch type { case .item: - let viewController = owner.itemFilterFactory.make() { result in - + let viewController = owner.itemFilterFactory.make { results in + reactor.action.onNext(.itemFilterOptionSelected(results)) + + if results.isEmpty { + owner.mainView.resetFilter() + } else { + owner.mainView.selectFilter() + } } owner.present(viewController, animated: true) case .monster: - let viewController = owner.monsterFilterFactory.make(startLevel: reactor.currentState.startLevel ?? 1, endLevel: reactor.currentState.endLevel ?? 200) { startLevel, endLevel in - + let viewController = owner.monsterFilterFactory.make(startLevel: reactor.currentState.startLevel ?? 0, endLevel: reactor.currentState.endLevel ?? 200) { startLevel, endLevel in + owner.mainView.selectFilter() reactor.action.onNext(.filterOptionSelected(startLevel: startLevel, endLevel: endLevel)) } owner.tabBarController?.presentModal(viewController) @@ -274,10 +282,10 @@ extension DictionaryListViewController: UICollectionViewDelegate, UICollectionVi public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let reactor = reactor else { return } let item: DictionaryMainItemResponse - + item = reactor.currentState.listItems[indexPath.item] let viewController: UIViewController - + switch reactor.currentState.type { case .total: // 전체 타입일 때는 item.type에 따라 분기 @@ -299,10 +307,9 @@ extension DictionaryListViewController: UICollectionViewDelegate, UICollectionVi // 단일 타입일 경우 리액터 타입에 따라 처리 viewController = detailFactory.make(type: reactor.currentState.type, id: item.id) } - navigationController?.pushViewController(viewController, animated: true) } - + public func scrollViewDidScroll(_ scrollView: UIScrollView) { let offsetY = scrollView.contentOffset.y let contentHeight = scrollView.contentSize.height diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchFactoryImpl.swift index 285e7bde..10857c85 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchFactoryImpl.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchFactoryImpl.swift @@ -7,7 +7,7 @@ public final class DictionarySearchFactoryImpl: DictionarySearchFactory { private let recentSearchAddUseCase: RecentSearchAddUseCase private let recentSearchFetchUseCase: RecentSearchFetchUseCase private let searchResultFactory: DictionarySearchResultFactory - + public init(recentSearchRemoveUseCase: RecentSearchRemoveUseCase, recentSearchAddUseCase: RecentSearchAddUseCase, searchResultFactory: DictionarySearchResultFactory, recentSearchFetchUseCase: RecentSearchFetchUseCase) { self.recentSearchRemoveUseCase = recentSearchRemoveUseCase self.recentSearchAddUseCase = recentSearchAddUseCase diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchReactor.swift index 576cbfeb..68b39c92 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchReactor.swift @@ -1,5 +1,5 @@ -import Foundation import DomainInterface +import Foundation import ReactorKit struct PopularItem { @@ -39,7 +39,7 @@ public final class DictionarySearchReactor: Reactor { let popularResult: [PopularItem] } - + public let recentSearchAddUseCase: RecentSearchAddUseCase public let recentSearchRemoveUseCase: RecentSearchRemoveUseCase public let recentSearchFetchUseCase: RecentSearchFetchUseCase @@ -47,8 +47,6 @@ public final class DictionarySearchReactor: Reactor { // MARK: - properties public var initialState: State var disposeBag = DisposeBag() - - // MARK: - init public init(recentSearchAddUseCase: RecentSearchAddUseCase, recentSearchRemoveUseCase: RecentSearchRemoveUseCase, recentSearchFetchUseCase: RecentSearchFetchUseCase) { @@ -76,11 +74,11 @@ public final class DictionarySearchReactor: Reactor { } let newItems = grid.flatMap { $0.compactMap { $0 } } - + self.recentSearchAddUseCase = recentSearchAddUseCase self.recentSearchRemoveUseCase = recentSearchRemoveUseCase self.recentSearchFetchUseCase = recentSearchFetchUseCase - + let savedRecentResult: [String] = [] self.initialState = State( @@ -89,7 +87,7 @@ public final class DictionarySearchReactor: Reactor { popularResult: newItems ) } - + // MARK: - Reactor Methods public func mutate(action: Action) -> Observable { switch action { diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchViewController.swift index 50539d9b..357dcc78 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearch/DictionarySearchViewController.swift @@ -176,13 +176,13 @@ extension DictionarySearchViewController { } } .disposed(by: disposeBag) - + rx.viewDidLoad .take(1) .map { Reactor.Action.viewDidLoad } .bind(to: reactor.action) .disposed(by: disposeBag) - + } } @@ -284,7 +284,7 @@ extension DictionarySearchViewController: UICollectionViewDelegate, UICollection for: indexPath ) as! PopularSearchHeaderView // TODO: 인기검색어 추후에 - //view.inject(mainText: "인기 검색어", subText: "업데이트 일자", hasRecent: reactor.currentState.hasRecent) + // view.inject(mainText: "인기 검색어", subText: "업데이트 일자", hasRecent: reactor.currentState.hasRecent) return view default: diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift index 72b88e05..57614151 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift @@ -30,14 +30,14 @@ public final class DictionarySearchResultReactor: Reactor { } var keyword: String? - + var counts: [Int] = [0, 0, 0, 0, 0, 0] } // MARK: - properties public var initialState: State var disposeBag = DisposeBag() - + // MARK: - UseCases private let dictionarySearchUseCase: FetchDictionarySearchListUseCase private let dictionarySearchCountUseCase: FetchDictionaryListCountUseCase @@ -67,14 +67,14 @@ public final class DictionarySearchResultReactor: Reactor { // 검색 결과 화면에서 재검색 시 case .searchButtonTapped(let keyword): let keyword = keyword ?? "" - + return Observable.just(.setKeyword(keyword)) } } - + public func reduce(state: State, mutation: Mutation) -> State { var newState = state - + switch mutation { case .navigateTo(let route): newState.route = route @@ -83,10 +83,10 @@ public final class DictionarySearchResultReactor: Reactor { case .setCounts(let counts): newState.counts = counts } - + return newState } - + public func transform(mutation: Observable) -> Observable { let keywordChanges = mutation .compactMap { mutation -> String? in diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift index 7a510c21..7b1cfa36 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift @@ -22,7 +22,7 @@ public final class DictionarySearchResultViewController: BaseViewController, Vie private var mainView: DictionaryMainView private let underLineController = TabBarUnderlineController() private let dictionaryListFactory: DictionaryMainListFactory - + private var didSetInitialIndex = false public init( @@ -43,12 +43,9 @@ public final class DictionarySearchResultViewController: BaseViewController, Vie @MainActor required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - - } - // MARK: - Life Cycle public extension DictionarySearchResultViewController { override func viewDidLoad() { @@ -63,8 +60,7 @@ public extension DictionarySearchResultViewController { didSetInitialIndex = true setInitialIndex() } - - + private func updateViewControllers(keyword: String) { guard let reactor = reactor else { return } let type = reactor.currentState.type @@ -103,7 +99,6 @@ public extension DictionarySearchResultViewController { } } - } // MARK: - SetUp @@ -154,11 +149,11 @@ private extension DictionarySearchResultViewController { ) mainView.tabCollectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredHorizontally) - + // 전체 레이아웃 강제 갱신 mainView.tabCollectionView.collectionViewLayout.invalidateLayout() mainView.tabCollectionView.layoutIfNeeded() - + DispatchQueue.main.async { [weak self] in self?.underLineController.setInitialIndicator() } @@ -177,7 +172,7 @@ public extension DictionarySearchResultViewController { .map { Reactor.Action.backbuttonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.searchBar.searchButton.rx.tap .withLatestFrom(mainView.searchBar.textField.rx.text.orEmpty) .map { text in Reactor.Action.searchButtonTapped(text) } @@ -209,12 +204,12 @@ public extension DictionarySearchResultViewController { owner.updateViewControllers(keyword: newKeyword) } .disposed(by: disposeBag) - + rx.viewWillAppear .map {_ in Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .map(\.counts) .distinctUntilChanged() @@ -288,4 +283,3 @@ extension DictionarySearchResultViewController: UICollectionViewDataSource, UICo underLineController.animateIndicatorToSelectedItem() } } - diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift index b33d8f99..e4a85934 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift @@ -2,11 +2,13 @@ import BaseFeature import DictionaryFeatureInterface public struct ItemFilterBottomSheetFactoryImpl: ItemFilterBottomSheetFactory { + private let sharedReactor = ItemFilterBottomSheetReactor() public init() {} - public func make(onFilterSelected: @escaping ([String]) -> Void) -> BaseViewController { + public func make(onFilterSelected: @escaping ([(String, String)]) -> Void) -> BaseViewController { + // let reactor = ItemFilterBottomSheetReactor() // 매번 새로 리액터를 생성하기 때문에 초기화 let viewController = ItemFilterBottomSheetViewController() - viewController.reactor = ItemFilterBottomSheetReactor() + viewController.reactor = sharedReactor viewController.onFilterSelected = onFilterSelected return viewController } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift index 7cacf94f..11d7ba3a 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift @@ -7,7 +7,8 @@ public final class ItemFilterBottomSheetReactor: Reactor { public enum Route { case none case dismiss - case dismissWithSelection([String]) + case dismissWithSelection([(String, String)]) + case resetFilters } // MARK: - Reactor @@ -16,7 +17,8 @@ public final class ItemFilterBottomSheetReactor: Reactor { case filterSelected(indexPath: IndexPath) case filterDeselected(indexPath: IndexPath) case changeLevelRange(low: Int, high: Int) - case applyButtonTapped([String]) + case applyButtonTapped([(String, String)]) + case resetFilters } public enum Mutation { @@ -25,24 +27,25 @@ public final class ItemFilterBottomSheetReactor: Reactor { case appendSelectedItem(indexPath: IndexPath) case removeSelectedItem(indexPath: IndexPath) case setLevelRange(low: Int, high: Int) + case resetFilters } public struct State { var sections: [String] = ["직업/레벨", "무기", "발사체", "방어구", "장신구", "주문서", "기타"] - var jobs: [String] = ["없음", "공용", "마법사", "전사", "궁수", "도적", "해적"] - var weapons: [String] = ["한손검", "한손도끼", "한손둔기", "창", "단검", "두손검", "두손도끼", "두손둔기", "풀암", "활", "석궁", "완드", "스태프", "아대"] - var projectiles: [String] = ["화살", "불릿", "표창"] - var armors: [String] = ["모자", "전신", "상의", "하의", "장갑", "신발", "방패", "전신 갑옷"] - var accessories: [String] = ["귀고리", "망토", "훈장", "눈장식", "얼굴장식", "팬던트", "벨트", "반지", "어깨장식", "귀장식"] + var jobs: [String] = ["없음", "공용", "마법사", "전사", "궁수", "도적"] + var weapons: [String] = ["한손검", "한손도끼", "한손둔기", "창", "단검", "두손검", "두손도끼", "두손둔기", "폴암", "활", "석궁", "완드", "스태프", "아대"] + var projectiles: [String] = ["화살", "표창"] // 불렛은 제거 + var armors: [String] = ["모자", "상의", "하의", "장갑", "신발", "방패", "전신갑옷"] // 전신 = 전신갑옷? + var accessories: [String] = ["귀고리", "망토", "훈장", "눈장식", "얼굴장식", "팬던트", "벨트", "반지", "어깨장식"] // 귀장식 데이터 안줌 -> 귀장식 = 귀고리, 훈장은 서버에서 데이터 안줌. @Pulse var scrollCategories: [String] = ["무기 주문서", "방어구 주문서", "기타 주문서"] - var originWeaponScrolls: [String] = ["한손검1", "한손검2", "한손검3", "한손검4", "한손검5", "한손검6", "한손검7", "한손검8", "한손검9", "한손검10"] - var originArmorScrolls: [String] = ["갑옷1", "갑옷2", "갑옷3", "갑옷4", "갑옷5", "갑옷6", "갑옷7", "갑옷8", "갑옷9", "갑옷10"] - var originEtcScrolls: [String] = ["기타1", "기타2", "기타3", "기타4", "기타5", "기타6", "기타7", "기타8", "기타9", "기타10"] + var originWeaponScrolls: [String] = ["한손검", "한손도끼", "한손둔기", "단검", "완드", "스태프", "두손검", "두손도끼", "두손둔기", "창", "폴암", "활", "석궁", "아대"] + var originArmorScrolls: [String] = ["투구", "상의", "하의", "전신갑옷", "신발", "장갑", "망토", "방패", "귀장식"] + var originEtcScrolls: [String] = ["펫장비", "연성서", "귀환주문서"] @Pulse var weaponScrolls: [String] = [] @Pulse var armorScrolls: [String] = [] @Pulse var etcScrolls: [String] = [] var etcItems: [String] = ["마스터리북", "스킬북", "소비", "설치", "이동수단"] - + var selectedScrollIndexes: Int? var selectedItemIndexes: [IndexPath] = [] var levelRange: (low: Int, high: Int) = (0, 200) @Pulse var route: Route = .none @@ -61,7 +64,8 @@ public final class ItemFilterBottomSheetReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .closeButtonTapped: - return Observable.just(.navigateTo(route: .dismiss)) + // 닫기버튼 누르면 선택했던 필터 초기화 해줘야함 + return .concat([Observable.just(.navigateTo(route: .dismiss)), Observable.just(.resetFilters)]) case .filterSelected(let indexPath): let section = ItemFilterBottomSheetViewController.FilterSection(rawValue: indexPath.section) switch section { @@ -82,6 +86,8 @@ public final class ItemFilterBottomSheetReactor: Reactor { return Observable.just(.setLevelRange(low: low, high: high)) case .applyButtonTapped(let selectedItems): return .just(.navigateTo(route: .dismissWithSelection(selectedItems))) + case .resetFilters: + return Observable.just(.resetFilters) } } @@ -114,6 +120,7 @@ public final class ItemFilterBottomSheetReactor: Reactor { "기타 주문서\(selectedEtcScrollCount == 0 ? "" : " \(selectedEtcScrollCount)")" ] case .setScrolls(let selectedIndex): + newState.selectedScrollIndexes = selectedIndex switch selectedIndex { case 0: newState.weaponScrolls = newState.originWeaponScrolls @@ -141,6 +148,14 @@ public final class ItemFilterBottomSheetReactor: Reactor { if !newState.selectedItemIndexes.contains(levelSection) { newState.selectedItemIndexes.insert(levelSection, at: 0) } } newState.levelRange = (low, high) + case .resetFilters: + newState.selectedItemIndexes = [] + newState.levelRange = (0, 200) + newState.scrollCategories = ["무기 주문서", "방어구 주문서", "기타 주문서"] + newState.weaponScrolls = [] + newState.armorScrolls = [] + newState.etcScrolls = [] + newState.selectedScrollIndexes = nil } return newState } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift index c588ff7b..ebbd4e11 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift @@ -78,9 +78,9 @@ public final class ItemFilterBottomSheetViewController: BaseViewController, View private var dataSource: DataSource! = nil private var mainView = ItemFilterBottomSheetView() private let underLineController = TabBarUnderlineController() - - public var onFilterSelected: (([String]) -> Void)? - + + public var onFilterSelected: (([(String, String)]) -> Void)? + override public init() { super.init() } @@ -108,6 +108,24 @@ public extension ItemFilterBottomSheetViewController { navigationController?.presentationController?.presentedView?.gestureRecognizers?.forEach { $0.isEnabled = false } presentationController?.presentedView?.gestureRecognizers?.forEach { $0.isEnabled = false } } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + guard let reactor = reactor else { return } + + reactor.currentState.selectedItemIndexes.forEach { indexPath in + let sectionCount = mainView.contentCollectionView.numberOfItems(inSection: indexPath.section) + if indexPath.row < sectionCount { + mainView.contentCollectionView.selectItem(at: indexPath, animated: false, scrollPosition: []) + } + + } + guard let selectedIndex = reactor.currentState.selectedScrollIndexes else { return } + // 스크롤 탭(무기/방어구/기타 주문서) UI 선택 상태 복원 + let scrollCategoryIndexPath = IndexPath(row: selectedIndex, section: FilterSection.scrollCategories.rawValue) + mainView.contentCollectionView.selectItem(at: scrollCategoryIndexPath, animated: false, scrollPosition: .centeredHorizontally) + + } } // MARK: - SetUp @@ -378,46 +396,60 @@ extension ItemFilterBottomSheetViewController { self?.view.endEditing(true) } .disposed(by: disposeBag) - + + mainView.clearButton.rx.tap + .withUnretained(self) + .subscribe(onNext: { owner, _ in + // Reactor에 액션 전달 + owner.reactor?.action.onNext(.resetFilters) + + // UI에서 직접 deselect + owner.mainView.contentCollectionView.indexPathsForSelectedItems?.forEach { + owner.mainView.contentCollectionView.deselectItem(at: $0, animated: false) + } + }) + .disposed(by: disposeBag) + mainView.headerView.firstIconButton.rx.tap .map { Reactor.Action.closeButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.applyButton.rx.tap .withUnretained(self) - .compactMap { owner, _ in - + .compactMap { _, _ in + let state = reactor.currentState - var selectedItems: [String] = [] + var selectedItems: [(String, String)] = [] for indexPath in state.selectedItemIndexes { guard let section = ItemFilterBottomSheetViewController.FilterSection(rawValue: indexPath.section) else { continue } switch section { case .level: - selectedItems.append("레벨 \(state.levelRange.low) ~ \(state.levelRange.high)") + selectedItems.append(("레벨", "레벨 \(state.levelRange.low) ~ \(state.levelRange.high)")) case .job: - selectedItems.append(state.jobs[indexPath.row]) + selectedItems.append(("직업", state.jobs[indexPath.row])) case .weapons: - selectedItems.append(state.weapons[indexPath.row]) + selectedItems.append(("무기", state.weapons[indexPath.row])) case .projectiles: - selectedItems.append(state.projectiles[indexPath.row]) + selectedItems.append(("발사체", state.projectiles[indexPath.row])) case .armors: - selectedItems.append(state.armors[indexPath.row]) + selectedItems.append(("방어구", state.armors[indexPath.row])) case .accessories: - selectedItems.append(state.accessories[indexPath.row]) + selectedItems.append(("장신구", state.accessories[indexPath.row])) case .scrollCategories: - selectedItems.append(state.scrollCategories[indexPath.row]) + selectedItems.append(("주문서", state.scrollCategories[indexPath.row])) case .weaponScrolls: - selectedItems.append(state.weaponScrolls[indexPath.row]) + // 기존 방식대로 하게 되면 주문서 탭 변경시 각 scrolls가 빈배열 처리되서 오류 + selectedItems.append(("무기주문서", state.originWeaponScrolls[indexPath.row])) case .armorsScrolls: - selectedItems.append(state.armorScrolls[indexPath.row]) + selectedItems.append(("방어구주문서", state.originArmorScrolls[indexPath.row])) case .etcScrolls: - selectedItems.append(state.etcScrolls[indexPath.row]) + selectedItems.append(("기타주문서", state.originEtcScrolls[indexPath.row])) case .etcItems: - selectedItems.append(state.etcItems[indexPath.row]) + selectedItems.append(("기타아이템", state.etcItems[indexPath.row])) } } return Reactor.Action.applyButtonTapped(selectedItems) @@ -434,6 +466,7 @@ extension ItemFilterBottomSheetViewController { .skip(1) .withUnretained(self) .subscribe { owner, scrolls in + guard let dataSource = owner.dataSource else { return } var snapshot = owner.dataSource.snapshot() snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .scrollCategories)) snapshot.appendItems(scrolls.scrollTypes.map { .scrollCategories($0) }, toSection: .scrollCategories) @@ -444,6 +477,7 @@ extension ItemFilterBottomSheetViewController { snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .etcScrolls)) snapshot.appendItems(scrolls.etcScrolls.map { .etcScrolls($0) }, toSection: .etcScrolls) owner.dataSource.apply(snapshot, animatingDifferences: false) { + guard let selectedItem = owner.getSelectedScrollCategoryIndexPath() else { return } var targetIndexPath: [IndexPath] = [] switch selectedItem.row { @@ -456,7 +490,13 @@ extension ItemFilterBottomSheetViewController { default: break } - for target in targetIndexPath { owner.mainView.contentCollectionView.selectItem(at: target, animated: true, scrollPosition: .left) } + for target in targetIndexPath { + let count = owner.mainView.contentCollectionView.numberOfItems(inSection: target.section) + if count > target.row { + owner.mainView.contentCollectionView.selectItem(at: target, animated: true, scrollPosition: .left) + } + } + } } .disposed(by: disposeBag) @@ -469,18 +509,17 @@ extension ItemFilterBottomSheetViewController { owner.mainView.selectedItemCollectionView.reloadData() } .disposed(by: disposeBag) - - rx.viewDidAppear + rx.viewDidLoad .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } - .withUnretained(self) - .subscribe { owner, route in + .subscribe { [weak self] route in + guard let self = self else { return } switch route { case .dismiss: - owner.dismiss(animated: true) + self.dismiss(animated: true) case .dismissWithSelection(let selectedItems): - owner.onFilterSelected?(selectedItems) - owner.dismiss(animated: true) + self.onFilterSelected?(selectedItems) + self.dismiss(animated: true) default: break } @@ -508,15 +547,32 @@ extension ItemFilterBottomSheetViewController: UICollectionViewDataSource { return cell } else { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TagChipCell.identifier, for: indexPath) as! TagChipCell - let titles = reactor.currentState.selectedItemIndexes.map { - guard let item = dataSource.itemIdentifier(for: $0) else { return "" } - switch item { - case .job(let title), .weapons(let title), .projectiles(let title), .armors(let title), .accessories(let title), - .scrollCategories(let title), .weaponScrolls(let title), .armorScrolls(let title), .etcScrolls(let title), .etcItems(let title): - return title + let titles = reactor.currentState.selectedItemIndexes.map { indexPath -> String in + guard let section = ItemFilterBottomSheetViewController.FilterSection(rawValue: indexPath.section) else { return "" } + switch section { + case .job: + return reactor.currentState.jobs[indexPath.row] + case .weapons: + return reactor.currentState.weapons[indexPath.row] + case .projectiles: + return reactor.currentState.projectiles[indexPath.row] + case .armors: + return reactor.currentState.armors[indexPath.row] + case .accessories: + return reactor.currentState.accessories[indexPath.row] + case .weaponScrolls: + return reactor.currentState.originWeaponScrolls[indexPath.row] + case .armorsScrolls: + return reactor.currentState.originArmorScrolls[indexPath.row] + case .etcScrolls: + return reactor.currentState.originEtcScrolls[indexPath.row] + case .etcItems: + return reactor.currentState.etcItems[indexPath.row] case .level: let range = reactor.currentState.levelRange return "\(range.low) ~ \(range.high)" + default: + return "" } } cell.inject(title: titles[indexPath.row], style: .normal) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/ItemFilterBottomSheetView.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/ItemFilterBottomSheetView.swift index 4eba7ab7..1e45758b 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/ItemFilterBottomSheetView.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/Views/ItemFilterBottomSheetView.swift @@ -49,7 +49,7 @@ final class ItemFilterBottomSheetView: UIView { }() public let applyButton: CommonButton = { - let button = CommonButton(style: .normal, title: "n개의 아이템 보기", disabledTitle: nil) + let button = CommonButton(style: .normal, title: "적용하기", disabledTitle: nil) return button }() diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetView.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetView.swift index 1cb8e7a2..e8e5e901 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetView.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/MonsterFilterBottomSheet/MonsterFilterBottomSheetView.swift @@ -53,7 +53,7 @@ public class MonsterFilterBottomSheetView: UIView { }() public let applyButton: CommonButton = { - let button = CommonButton(style: .normal, title: "n개의 아이템 보기", disabledTitle: nil) + let button = CommonButton(style: .normal, title: "적용하기", disabledTitle: nil) return button }() diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift index 28bfc345..48195a79 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift @@ -72,7 +72,7 @@ private extension AppDelegate { DIContainer.register(type: BookmarkRepository.self) { BookmarkRepositoryImpl(provider: DIContainer.resolve(type: NetworkProvider.self), interceptor: TokenInterceptor(fetchTokenUseCase: DIContainer.resolve(type: FetchTokenFromLocalUseCase.self))) } - + DIContainer.register(type: UserDefaultsRepository.self) { UserDefaultsRepositoryImpl() } @@ -213,6 +213,9 @@ private extension AppDelegate { DIContainer.register(type: FetchNPCBookmarkUseCase.self) { FetchNPCBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) } + DIContainer.register(type: FetchBookmarkUseCase.self) { + FetchBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) + } DIContainer.register(type: FetchDictionarySearchListUseCase.self) { FetchDictionarySearchListUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) } @@ -228,11 +231,10 @@ private extension AppDelegate { DIContainer.register(type: RecentSearchFetchUseCase.self) { RecentSearchFetchUseCaseImpl(repository: DIContainer.resolve(type: UserDefaultsRepository.self)) } - DIContainer.register(type: FetchBookmarkUseCase.self) { - FetchBookmarkUseCaseImpl(repository: DIContainer.resolve(type: BookmarkRepository.self)) + DIContainer.register(type: ParseItemFilterResultUseCase.self) { + ParseItemFilterResultUseCaseImpl() } } - func registerFactory() { DIContainer.register(type: ItemFilterBottomSheetFactory.self) { ItemFilterBottomSheetFactoryImpl() @@ -259,6 +261,7 @@ private extension AppDelegate { dictionaryNpcListItemUseCase: DIContainer.resolve(type: FetchDictionaryNpcListUseCase.self), dictionaryListItemUseCase: DIContainer.resolve(type: FetchDictionaryMonsterListUseCase.self), setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), + parseItemFilterResultUseCase: DIContainer.resolve(type: ParseItemFilterResultUseCase.self), itemFilterFactory: DIContainer.resolve(type: ItemFilterBottomSheetFactory.self), monsterFilterFactory: DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self), sortedFactory: DIContainer.resolve(type: SortedBottomSheetFactory.self), @@ -276,8 +279,8 @@ private extension AppDelegate { } DIContainer.register(type: DictionarySearchFactory.self) { DictionarySearchFactoryImpl(recentSearchRemoveUseCase: DIContainer.resolve(type: RecentSearchRemoveUseCase.self), - recentSearchAddUseCase: DIContainer.resolve(type: RecentSearchAddUseCase.self), - searchResultFactory: DIContainer + recentSearchAddUseCase: DIContainer.resolve(type: RecentSearchAddUseCase.self), + searchResultFactory: DIContainer .resolve(type: DictionarySearchResultFactory.self), recentSearchFetchUseCase: DIContainer.resolve(type: RecentSearchFetchUseCase.self) ) } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/ViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/ViewController.swift index 2628f0a4..28d2d119 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/ViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/ViewController.swift @@ -19,7 +19,7 @@ class ViewController: UIViewController { }() lazy var views: [[UIViewController]] = { - let itemFilterBottomSheetVC = DIContainer.resolve(type: ItemFilterBottomSheetFactory.self).make() { _ in } + let itemFilterBottomSheetVC = DIContainer.resolve(type: ItemFilterBottomSheetFactory.self).make { _ in } itemFilterBottomSheetVC.title = "아이템 필터 바텀시트" let monsterBottomSheetVC = DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self).make(startLevel: 0, endLevel: 200) { _, _ in } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/ItemFilterBottomSheetFactory.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/ItemFilterBottomSheetFactory.swift index d4ca05b5..37257544 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/ItemFilterBottomSheetFactory.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeatureInterface/ItemFilterBottomSheetFactory.swift @@ -1,5 +1,5 @@ import BaseFeature public protocol ItemFilterBottomSheetFactory { - func make(onFilterSelected: @escaping ([String]) -> Void) -> BaseViewController + func make(onFilterSelected: @escaping ([(String, String)]) -> Void) -> BaseViewController }