From 6fe873ee13555ca819534fb5361cba846c81841f Mon Sep 17 00:00:00 2001 From: duthd3 Date: Sat, 1 Nov 2025 11:34:41 +0900 Subject: [PATCH 1/4] itemFilter --- .../DictionaryListReactor.swift | 19 +++++++++-- .../DictionaryListViewController.swift | 33 +++++++++++++++++-- .../ItemFilterBottomSheetFactoryImpl.swift | 2 +- .../ItemFilterBottomSheetReactor.swift | 4 +-- .../ItemFilterBottomSheetViewController.swift | 31 ++++++++--------- .../DictionaryFeatureDemo/AppDelegate.swift | 3 +- .../ItemFilterBottomSheetFactory.swift | 2 +- 7 files changed, 68 insertions(+), 26 deletions(-) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift index b868a8b0..7e701cd9 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift @@ -19,10 +19,12 @@ open class DictionaryListReactor: Reactor { case filterButtonTapped case sortOptionSelected(SortType) // 정렬 옵션 선택 시 액션 case filterOptionSelected(startLevel: Int, endLevel: Int) // 필터 옵션 선택 시 액션 + case itemFilterOptionSelected(keyword: String?, jobId: Int?, startLevel: Int?, endLevel: Int?) case setCurrentPage case fetchList // data 불러오기 case fetchListFilter // 필터링된 data 불러오기 case undoLastDeletedBookmark + case setJobId(Int) // case itemFilterOptionSelected([String]) // 아이템 필터 옵션 선택 시 액션 } @@ -39,6 +41,7 @@ open class DictionaryListReactor: Reactor { case initPage case setLoginState(Bool) case setLastDeletedBookmark(DictionaryMainItemResponse?) + case setJobId(Int) // jobId 설정 } // MARK: - State @@ -132,7 +135,7 @@ open class DictionaryListReactor: Reactor { self.dictionaryListUseCase .execute( type: .monster, - query: DictionaryListQuery(keyword: self.currentState.keyword ?? "", page: self.currentState.currentPage, size: 20, sort: nil) + query: DictionaryListQuery(keyword: self.currentState.keyword ?? "", page: self.currentState.currentPage, size: 20, sort: currentState.sort) ) .map { Mutation.setListItem($0) } ]) @@ -154,7 +157,7 @@ open class DictionaryListReactor: Reactor { fetchMutation = Observable.concat([ .just(.initPage), self.dictionaryItemListUseCase - .execute(keyword: self.currentState.keyword ?? "", jobId: nil, minLevel: nil, maxLevel: nil, categoryIds: nil, page: self.currentState.currentPage, size: 20, sort: nil) + .execute(keyword: self.currentState.keyword ?? "", jobId: nil, minLevel: nil, maxLevel: nil, categoryIds: nil, page: self.currentState.currentPage, size: 20, sort: currentState.sort) .map { Mutation.setListItem($0) } ]) case .map: @@ -243,6 +246,15 @@ open class DictionaryListReactor: Reactor { .just(.setLastDeletedBookmark(nil)) ]) ) + case .itemFilterOptionSelected(let keyword, let jobId, let startLevel, let endLevel): + return .concat([Observable.just(.setJobId(jobId ?? 0)), Observable.just(.setFilter(start: startLevel ?? 1, end: endLevel ?? 200)), .just(.initPage)]) + .concat(Observable.deferred { [weak self] in + guard let self = self else { return .empty()} + return self.fetchListFilter(sort: self.currentState.sort, startLevel: currentState.startLevel ?? 1, endLevel: currentState.endLevel ?? 200) + }) + case .setJobId(let id): + return Observable.just(.setJobId(id)) + } } @@ -341,6 +353,9 @@ open class DictionaryListReactor: Reactor { newState.lastDeletedBookmark = item case .setLoginState(let isLogin): newState.isLogin = isLogin + case .setJobId(let id): + print("새로운 잡아이디:\(id)") + newState.jobId = id } return newState } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift index 212ef8f2..4b27c454 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift @@ -144,9 +144,37 @@ extension DictionaryListViewController { case .filter(let type): switch type { case .item: - let viewController = owner.itemFilterFactory.make() { result in + let viewController = owner.itemFilterFactory.make() { results in - print("DictionaryList에서 결과 받음:\(result)") + // TODO: 해당 결과 이용해서 필터 걸고 API 호출해서 데이터 상태 변경 + let jobId = ["전사": 100, "마법사": 200, "도적": 400, "궁수": 300] + + for result in results { + + switch result.0 { + case "직업": + print("jobId:\(jobId[result.1])") + reactor.action.onNext(.setJobId(jobId[result.1] ?? 0)) + case "레벨": + let resultText = result.1 + print("resultText: \(resultText)") + // "레벨:" 제거 + let levelText = resultText.replacingOccurrences(of: "레벨", with: "").trimmingCharacters(in: .whitespaces) + + // "~" 기준으로 분리 + let parts = levelText.components(separatedBy: "~").map { $0.trimmingCharacters(in: .whitespaces) } + + if let low = Int(parts.first ?? ""), let high = Int(parts.last ?? "") { + print("low:", low) // 15 + print("high:", high) // 186 + reactor.action.onNext(.filterOptionSelected(startLevel: low, endLevel: high)) + } + default: + break + } + } + print("최종 호출전 잡아이디: \(reactor.currentState.jobId)") +// reactor.action.onNext(.itemFilterOptionSelected(keyword: reactor.currentState.keyword, jobId: reactor.currentState.jobId, startLevel: reactor.currentState.startLevel, endLevel: reactor.currentState.endLevel)) } owner.present(viewController, animated: true) case .monster: @@ -300,7 +328,6 @@ extension DictionaryListViewController: UICollectionViewDelegate, UICollectionVi // 단일 타입일 경우 리액터 타입에 따라 처리 viewController = detailFactory.make(type: reactor.currentState.type, id: item.id) } - navigationController?.pushViewController(viewController, animated: true) } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift index b33d8f99..75a0ad4c 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift @@ -4,7 +4,7 @@ import DictionaryFeatureInterface public struct ItemFilterBottomSheetFactoryImpl: ItemFilterBottomSheetFactory { public init() {} - public func make(onFilterSelected: @escaping ([String]) -> Void) -> BaseViewController { + public func make(onFilterSelected: @escaping ([(String, String)]) -> Void) -> BaseViewController { let viewController = ItemFilterBottomSheetViewController() viewController.reactor = ItemFilterBottomSheetReactor() viewController.onFilterSelected = onFilterSelected diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift index 7a71bc13..e61f0017 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift @@ -7,7 +7,7 @@ public final class ItemFilterBottomSheetReactor: Reactor { public enum Route { case none case dismiss - case dismissWithSelection([String]) + case dismissWithSelection([(String, String)]) } // MARK: - Reactor @@ -16,7 +16,7 @@ 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)]) } public enum Mutation { diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift index 3288b353..c55637ed 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift @@ -79,7 +79,7 @@ public final class ItemFilterBottomSheetViewController: BaseViewController, View private var mainView = ItemFilterBottomSheetView() private let underLineController = TabBarUnderlineController() - public var onFilterSelected: (([String]) -> Void)? + public var onFilterSelected: (([(String, String)]) -> Void)? override public init() { super.init() @@ -390,39 +390,40 @@ extension ItemFilterBottomSheetViewController { 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]) + selectedItems.append(("무기주문서",state.weaponScrolls[indexPath.row])) case .armorsScrolls: - selectedItems.append(state.armorScrolls[indexPath.row]) + selectedItems.append(("방어구주문서",state.armorScrolls[indexPath.row])) case .etcScrolls: - selectedItems.append(state.etcScrolls[indexPath.row]) + selectedItems.append(("기타주문서",state.etcScrolls[indexPath.row])) case .etcItems: - selectedItems.append(state.etcItems[indexPath.row]) + selectedItems.append(("기타아이템",state.etcItems[indexPath.row])) } } - print("✅ 선택된 아이템들:") - selectedItems.forEach { print($0) } + for item in selectedItems { + print("\(item.0): \(item.1)") + } return Reactor.Action.applyButtonTapped(selectedItems) } .bind(to: reactor.action) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift index ca85f76f..52b5679a 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift @@ -255,8 +255,7 @@ private extension AppDelegate { dictionaryListCountUseCase: DIContainer.resolve(type: FetchDictionaryListCountUseCase.self), dictionaryMainListFactory: DIContainer .resolve(type: DictionaryMainListFactory.self), - dictionarySearchListUseCase: DIContainer.resolve(type: FetchDictionarySearchListUseCase.self), - + dictionarySearchListUseCase: DIContainer.resolve(type: FetchDictionarySearchListUseCase.self) ) } DIContainer.register(type: DictionarySearchFactory.self) { 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 } From ddeb8843f7094376e14fd6a85400989f18665718 Mon Sep 17 00:00:00 2001 From: duthd3 Date: Sat, 8 Nov 2025 13:32:01 +0900 Subject: [PATCH 2/4] itemFilterDemo --- .../Endpoints/DictionaryListEndPoint.swift | 10 ++- .../DictionaryListAPIRepositoryImpl.swift | 4 +- .../FetchDictionaryItemListUseCaseImpl.swift | 2 +- .../ParseItemFilterResultUseCaseImpl.swift | 61 +++++++++++++++++++ .../DictionaryList/DictionaryListQuery.swift | 8 +-- .../DictionaryList/ItemFilterCriteria.swift | 13 ++++ .../DictionaryListAPIRepository.swift | 2 +- .../FetchDictionaryItemListUseCase.swift | 2 +- .../ParseItemFilterUseCase.swift | 3 + .../BaseFeature/SharedView/BaseListView.swift | 7 ++- .../BookmarkListViewController.swift | 2 +- .../DictionaryListFactoryImpl.swift | 8 ++- .../DictionaryListReactor.swift | 50 +++++++++------ .../DictionaryListViewController.swift | 43 +++---------- .../ItemFilterBottomSheetFactoryImpl.swift | 3 +- .../ItemFilterBottomSheetReactor.swift | 59 ++++++++++++++++-- .../ItemFilterBottomSheetViewController.swift | 4 -- .../DictionaryFeatureDemo/AppDelegate.swift | 4 ++ .../ViewController.swift | 2 +- 19 files changed, 205 insertions(+), 82 deletions(-) create mode 100644 MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift create mode 100644 MLS/Domain/DomainInterface/Entity/DictionaryList/ItemFilterCriteria.swift create mode 100644 MLS/Domain/DomainInterface/UseCase/DictionaryList/ParseItemFilterUseCase.swift diff --git a/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift b/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift index 997c2bb3..34ca9d81 100644 --- a/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift @@ -17,7 +17,7 @@ public enum DictionaryListEndPoint { } // 몬스터 리스트 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,12 @@ 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/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/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/ParseItemFilterResultUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift new file mode 100644 index 00000000..ccde0fa9 --- /dev/null +++ b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift @@ -0,0 +1,61 @@ +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 + ] + + public init() {} + + public func execute(results: [(String, String)]) -> ItemFilterCriteria { + var jobIds: [Int] = [] + var startLevel: Int? + var endLevel: Int? + var categoryIds: [Int] = [] + + for (key, value) in results { + switch key { + case "직업": + if let id = jobIdMap[value] { + 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 ?? "") { + startLevel = low + endLevel = high + } + + case "무기", "발사체": + if let id = categoryIdMap[value] { + categoryIds.append(id) + } + + case "무기주문서", "방어구주문서", "기타주문서": + if let id = categoryIdMap[value + "주문서"] { + categoryIds.append(id) + } + + default: + break + } + } + + return ItemFilterCriteria(jobIds: jobIds, startLevel: startLevel, endLevel: endLevel, categoryIds: categoryIds) + } +} 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..55074625 --- /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/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..46022ffa 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift +++ b/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift @@ -148,10 +148,15 @@ 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 checkEmptyData(isEmpty: Bool) { emptyView.isHidden = !isEmpty diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift index 0f30a3b9..c1843c55 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift @@ -138,7 +138,7 @@ extension BookmarkListViewController { let viewController = owner.sortedFactory.make(sortedOptions: type.bookmarkSortedFilter, selectedIndex: owner.selectedSortIndex) { index in owner.selectedSortIndex = index let selectedFilter = reactor.currentState.type.bookmarkSortedFilter[index] - owner.mainView.selectFilter(selectedType: selectedFilter) + owner.mainView.selectSort(selectedType: selectedFilter) } owner.tabBarController?.presentModal(viewController) case .filter(let type): diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift index cf196293..f1f9d94a 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, + partItemFilterResultUseCase: 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 = partItemFilterResultUseCase self.itemFilterFactory = itemFilterFactory self.monsterFilterFactory = monsterFilterFactory self.sortedFactory = sortedFactory @@ -61,7 +63,9 @@ 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 7e701cd9..2c6ae799 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift @@ -19,13 +19,11 @@ open class DictionaryListReactor: Reactor { case filterButtonTapped case sortOptionSelected(SortType) // 정렬 옵션 선택 시 액션 case filterOptionSelected(startLevel: Int, endLevel: Int) // 필터 옵션 선택 시 액션 - case itemFilterOptionSelected(keyword: String?, jobId: Int?, startLevel: Int?, endLevel: Int?) + case itemFilterOptionSelected([(String, String)]) case setCurrentPage case fetchList // data 불러오기 case fetchListFilter // 필터링된 data 불러오기 case undoLastDeletedBookmark - case setJobId(Int) - // case itemFilterOptionSelected([String]) // 아이템 필터 옵션 선택 시 액션 } // MARK: - Mutation @@ -36,12 +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 setJobId([Int]) // jobId 설정 + case setCategoryId([Int]) } // MARK: - State @@ -52,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 @@ -78,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() @@ -92,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 @@ -103,6 +104,7 @@ open class DictionaryListReactor: Reactor { self.dictionaryNpcListUseCase = dictionaryNpcListUseCase self.dictionaryListUseCase = dictionaryListUseCase self.setBookmarkUseCase = setBookmarkUseCase + self.parseItemFilterResultUseCase = parseItemFilterResultUseCase } // MARK: - Mutate @@ -246,20 +248,27 @@ open class DictionaryListReactor: Reactor { .just(.setLastDeletedBookmark(nil)) ]) ) - case .itemFilterOptionSelected(let keyword, let jobId, let startLevel, let endLevel): - return .concat([Observable.just(.setJobId(jobId ?? 0)), Observable.just(.setFilter(start: startLevel ?? 1, end: endLevel ?? 200)), .just(.initPage)]) + 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: currentState.startLevel ?? 1, endLevel: currentState.endLevel ?? 200) + guard let self = self else { return .empty() } + return self.fetchListFilter( + sort: self.currentState.sort, + startLevel: self.currentState.startLevel, + endLevel: self.currentState.endLevel + ) }) - case .setJobId(let id): - return Observable.just(.setJobId(id)) - } } // 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 @@ -270,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, @@ -354,8 +363,9 @@ open class DictionaryListReactor: Reactor { case .setLoginState(let isLogin): newState.isLogin = isLogin case .setJobId(let id): - print("새로운 잡아이디:\(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 4b27c454..7ab01699 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift @@ -117,11 +117,13 @@ 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) - rx.viewWillAppear + rx.viewDidLoad .map { _ in Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) @@ -137,49 +139,22 @@ 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() { results in - - // TODO: 해당 결과 이용해서 필터 걸고 API 호출해서 데이터 상태 변경 - let jobId = ["전사": 100, "마법사": 200, "도적": 400, "궁수": 300] - for result in results { - - switch result.0 { - case "직업": - print("jobId:\(jobId[result.1])") - reactor.action.onNext(.setJobId(jobId[result.1] ?? 0)) - case "레벨": - let resultText = result.1 - print("resultText: \(resultText)") - // "레벨:" 제거 - let levelText = resultText.replacingOccurrences(of: "레벨", with: "").trimmingCharacters(in: .whitespaces) - - // "~" 기준으로 분리 - let parts = levelText.components(separatedBy: "~").map { $0.trimmingCharacters(in: .whitespaces) } - - if let low = Int(parts.first ?? ""), let high = Int(parts.last ?? "") { - print("low:", low) // 15 - print("high:", high) // 186 - reactor.action.onNext(.filterOptionSelected(startLevel: low, endLevel: high)) - } - default: - break - } - } - print("최종 호출전 잡아이디: \(reactor.currentState.jobId)") -// reactor.action.onNext(.itemFilterOptionSelected(keyword: reactor.currentState.keyword, jobId: reactor.currentState.jobId, startLevel: reactor.currentState.startLevel, endLevel: reactor.currentState.endLevel)) + let viewController = owner.itemFilterFactory.make() { results in + reactor.action.onNext(.itemFilterOptionSelected(results)) + owner.mainView.selectFilter() } owner.present(viewController, animated: true) case .monster: 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) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift index 75a0ad4c..581ec448 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift @@ -5,8 +5,9 @@ public struct ItemFilterBottomSheetFactoryImpl: ItemFilterBottomSheetFactory { public init() {} public func make(onFilterSelected: @escaping ([(String, String)]) -> Void) -> BaseViewController { + let reactor = ItemFilterBottomSheetReactor() let viewController = ItemFilterBottomSheetViewController() - viewController.reactor = ItemFilterBottomSheetReactor() + viewController.reactor = reactor viewController.onFilterSelected = onFilterSelected return viewController } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift index e61f0017..1942a293 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift @@ -29,15 +29,15 @@ public final class ItemFilterBottomSheetReactor: Reactor { public struct State { var sections: [String] = ["직업/레벨", "무기", "발사체", "방어구", "장신구", "주문서", "기타"] - var jobs: [String] = ["없음", "공용", "마법사", "전사", "궁수", "도적", "해적"] + var jobs: [String] = ["없음", "공용", "마법사", "전사", "궁수", "도적"] var weapons: [String] = ["한손검", "한손도끼", "한손둔기", "창", "단검", "두손검", "두손도끼", "두손둔기", "풀암", "활", "석궁", "완드", "스태프", "아대"] - var projectiles: [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] = [] @@ -56,6 +56,17 @@ public final class ItemFilterBottomSheetReactor: Reactor { public init() { self.initialState = State() } + // 필터 선택시 상태복원 할 init + public init(initialSelections: [(String, String)] = [], + initialLevelRange: (low: Int, high: Int) = (0, 200)) { + var state = State() + print("initialSection:\(initialSelections)") + state.selectedItemIndexes = [[0,2],[1, 2]] + print("선택된 아이템들:\(state.selectedItemIndexes)") + state.levelRange = initialLevelRange + + self.initialState = state + } // MARK: - Reactor Methods public func mutate(action: Action) -> Observable { @@ -141,8 +152,44 @@ public final class ItemFilterBottomSheetReactor: Reactor { if !newState.selectedItemIndexes.contains(levelSection) { newState.selectedItemIndexes.insert(levelSection, at: 0) } } newState.levelRange = (low, high) - print("level설정: \(low), \(high)") } return newState } + + // MARK: - Static Helper + private static func mapSelectionsToIndexPaths(_ selections: [(String, String)], from state: State) -> [IndexPath] { + var indexPaths: [IndexPath] = [] + + for selection in selections { + let (sectionName, itemName) = selection + guard let section = ItemFilterBottomSheetViewController.FilterSection.allCases.first(where: { $0.headerTitle == sectionName }) else { + continue + } + + let row: Int? + switch section { + case .job: + row = state.jobs.firstIndex(of: itemName) + case .weapons: + row = state.weapons.firstIndex(of: itemName) + case .projectiles: + row = state.projectiles.firstIndex(of: itemName) + case .armors: + row = state.armors.firstIndex(of: itemName) + case .accessories: + row = state.accessories.firstIndex(of: itemName) + case .scrollCategories: + row = state.scrollCategories.firstIndex(of: itemName) + case .etcItems: + row = state.etcItems.firstIndex(of: itemName) + default: + row = nil + } + + if let row = row { + indexPaths.append(IndexPath(row: row, section: section.rawValue)) + } + } + return indexPaths + } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift index c55637ed..20b97560 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift @@ -420,10 +420,6 @@ extension ItemFilterBottomSheetViewController { selectedItems.append(("기타아이템",state.etcItems[indexPath.row])) } } - - for item in selectedItems { - print("\(item.0): \(item.1)") - } return Reactor.Action.applyButtonTapped(selectedItems) } .bind(to: reactor.action) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift index 52b5679a..f452af28 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift @@ -215,6 +215,9 @@ private extension AppDelegate { DIContainer.register(type: FetchDictionaryListCountUseCase.self) { FetchDictionaryListCountUseCaseImpl(repository: DIContainer.resolve(type: DictionaryListAPIRepository.self)) } + DIContainer.register(type: ParseItemFilterResultUseCase.self) { + ParseItemFilterResultUseCaseImpl() + } } func registerFactory() { @@ -243,6 +246,7 @@ private extension AppDelegate { dictionaryNpcListItemUseCase: DIContainer.resolve(type: FetchDictionaryNpcListUseCase.self), dictionaryListItemUseCase: DIContainer.resolve(type: FetchDictionaryMonsterListUseCase.self), setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), + partItemFilterResultUseCase: DIContainer.resolve(type: ParseItemFilterResultUseCase.self), itemFilterFactory: DIContainer.resolve(type: ItemFilterBottomSheetFactory.self), monsterFilterFactory: DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self), sortedFactory: DIContainer.resolve(type: SortedBottomSheetFactory.self), diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/ViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/ViewController.swift index 2628f0a4..b2887683 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(initialSelections: [], initialLevelRange: (0, 200)) { _ in } itemFilterBottomSheetVC.title = "아이템 필터 바텀시트" let monsterBottomSheetVC = DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self).make(startLevel: 0, endLevel: 200) { _, _ in } From 14458c70f4cbc78c4cfe051feac772898c002e6f Mon Sep 17 00:00:00 2001 From: duthd3 Date: Sat, 8 Nov 2025 13:46:23 +0900 Subject: [PATCH 3/4] bookmark dependency inject --- .../DictionaryFeatureDemo/AppDelegate.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift index 3847530f..5319a205 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift @@ -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)) } @@ -359,7 +362,7 @@ private extension AppDelegate { AddCollectionFactoryImpl() } DIContainer.register(type: BookmarkListFactory.self) { - BookmarkListFactoryImpl(itemFilterFactory: DIContainer.resolve(type: ItemFilterBottomSheetFactory.self), monsterFilterFactory: DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self), sortedFactory: DIContainer.resolve(type: SortedBottomSheetFactory.self), bookmarkModalFactory: DIContainer.resolve(type: BookmarkModalFactory.self), loginFactory: DIContainer.resolve(type: LoginFactory.self), setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), checkLoginUseCase: DIContainer.resolve(type: CheckLoginUseCase.self), fetchMonsterBookmarkUseCase: DIContainer.resolve(type: FetchMonsterBookmarkUseCase.self), fetchItemBookmarkUseCase: DIContainer.resolve(type: FetchItemBookmarkUseCase.self), fetchNPCBookmarkUseCase: DIContainer.resolve(type: FetchNPCBookmarkUseCase.self), fetchQuestBookmarkUseCase: DIContainer.resolve(type: FetchQuestBookmarkUseCase.self), fetchMapBookmarkUseCase: DIContainer.resolve(type: FetchMapBookmarkUseCase.self)) + BookmarkListFactoryImpl(itemFilterFactory: DIContainer.resolve(type: ItemFilterBottomSheetFactory.self), monsterFilterFactory: DIContainer.resolve(type: MonsterFilterBottomSheetFactory.self), sortedFactory: DIContainer.resolve(type: SortedBottomSheetFactory.self), bookmarkModalFactory: DIContainer.resolve(type: BookmarkModalFactory.self), loginFactory: DIContainer.resolve(type: LoginFactory.self), setBookmarkUseCase: DIContainer.resolve(type: SetBookmarkUseCase.self), checkLoginUseCase: DIContainer.resolve(type: CheckLoginUseCase.self), fetchBookmarkUseCase: DIContainer.resolve(type: FetchBookmarkUseCase.self), fetchMonsterBookmarkUseCase: DIContainer.resolve(type: FetchMonsterBookmarkUseCase.self), fetchItemBookmarkUseCase: DIContainer.resolve(type: FetchItemBookmarkUseCase.self), fetchNPCBookmarkUseCase: DIContainer.resolve(type: FetchNPCBookmarkUseCase.self), fetchQuestBookmarkUseCase: DIContainer.resolve(type: FetchQuestBookmarkUseCase.self), fetchMapBookmarkUseCase: DIContainer.resolve(type: FetchMapBookmarkUseCase.self)) } DIContainer.register(type: CollectionSettingFactory.self) { CollectionSettingFactoryImpl() From 31d25a7d28873beccb430ee7bc07179fdc1ac0e7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 12 Nov 2025 15:18:16 +0000 Subject: [PATCH 4/4] style/#266: Apply SwiftLint autocorrect --- .../DTO/DictionaryDTO/DictionaryAllDTO.swift | 1 - .../DTO/SearchCountDTO/SearchCountDTO.swift | 2 +- .../Endpoints/DictionaryListEndPoint.swift | 4 +- .../Network/NetworkProviderImpl.swift | 2 +- .../UserDefaultsRepositoryImpl.swift | 8 +-- .../FetchDictionaryAllListUseCaseImpl.swift | 6 +- ...FetchDictionarySearchListUseCaseImpl.swift | 4 +- .../ParseItemFilterResultUseCaseImpl.swift | 4 +- .../FetchDictionaryListCountUseCaseImpl.swift | 4 +- .../RecentSearchAddUseCaseImpl.swift | 6 +- .../RecentSearchFetchUseCaseImpl.swift | 4 +- .../DictionaryList/ItemFilterCriteria.swift | 2 +- .../SearchCount/SearchCountResponse.swift | 2 +- .../BaseFeature/SharedView/BaseListView.swift | 4 +- .../BookmarkListViewController.swift | 4 +- .../DictionaryDetailBaseViewController.swift | 68 +++++++++---------- .../ItemDictionaryDetailViewController.swift | 4 +- ...onsterDictionaryDetailViewController.swift | 2 +- .../DictionaryListReactor.swift | 2 +- .../DictionaryListViewController.swift | 16 ++--- .../DictionarySearchFactoryImpl.swift | 2 +- .../DictionarySearchReactor.swift | 12 ++-- .../DictionarySearchViewController.swift | 6 +- .../DictionarySearchResultReactor.swift | 14 ++-- ...DictionarySearchResultViewController.swift | 20 ++---- .../ItemFilterBottomSheetFactoryImpl.swift | 2 +- .../ItemFilterBottomSheetReactor.swift | 2 +- .../ItemFilterBottomSheetViewController.swift | 44 ++++++------ .../DictionaryFeatureDemo/AppDelegate.swift | 12 ++-- .../ViewController.swift | 2 +- 30 files changed, 127 insertions(+), 138 deletions(-) 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 41ddd96c..e6831513 100644 --- a/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift @@ -11,8 +11,8 @@ 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) } // 몬스터 리스트 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/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/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 index b18fbef7..b2a9bf37 100644 --- a/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/DictionaryList/ParseItemFilterResultUseCaseImpl.swift @@ -19,9 +19,9 @@ public final class ParseItemFilterResultUseCaseImpl: ParseItemFilterResultUseCas "귀장식주문서": 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]()) 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/ItemFilterCriteria.swift b/MLS/Domain/DomainInterface/Entity/DictionaryList/ItemFilterCriteria.swift index 55074625..8fe61cd6 100644 --- a/MLS/Domain/DomainInterface/Entity/DictionaryList/ItemFilterCriteria.swift +++ b/MLS/Domain/DomainInterface/Entity/DictionaryList/ItemFilterCriteria.swift @@ -3,7 +3,7 @@ public struct ItemFilterCriteria { 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 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/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift b/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift index 6af56f3b..d2aa8973 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift +++ b/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift @@ -152,12 +152,12 @@ public extension BaseListView { 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 diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift index e7a780bd..25ae0aa1 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift @@ -142,8 +142,8 @@ extension BookmarkListViewController { 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/DictionaryListReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift index 1a8753e1..4fa5f65b 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift @@ -113,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 { diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift index 85e9406d..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) @@ -119,7 +119,7 @@ extension DictionaryListViewController { self?.mainView.listCollectionView.isHidden = item.isEmpty // 보여줄 item이 없을 경우, 터치를 막는데 왜 막는건지? // 몬스터나 아이템 탭에서 필터링을 하다가 item이 없을 경우, 필터 버튼도 터치가 안되서 계속 item 없음 - //self?.mainView.isUserInteractionEnabled = !item.isEmpty + // self?.mainView.isUserInteractionEnabled = !item.isEmpty }) .disposed(by: disposeBag) @@ -146,7 +146,7 @@ extension DictionaryListViewController { case .filter(let type): switch type { case .item: - let viewController = owner.itemFilterFactory.make() { results in + let viewController = owner.itemFilterFactory.make { results in reactor.action.onNext(.itemFilterOptionSelected(results)) if results.isEmpty { @@ -282,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에 따라 분기 @@ -309,7 +309,7 @@ extension DictionaryListViewController: UICollectionViewDelegate, UICollectionVi } 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 1777f4db..e4a85934 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetFactoryImpl.swift @@ -6,7 +6,7 @@ public struct ItemFilterBottomSheetFactoryImpl: ItemFilterBottomSheetFactory { public init() {} public func make(onFilterSelected: @escaping ([(String, String)]) -> Void) -> BaseViewController { - //let reactor = ItemFilterBottomSheetReactor() // 매번 새로 리액터를 생성하기 때문에 초기화 + // let reactor = ItemFilterBottomSheetReactor() // 매번 새로 리액터를 생성하기 때문에 초기화 let viewController = ItemFilterBottomSheetViewController() viewController.reactor = sharedReactor viewController.onFilterSelected = onFilterSelected diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift index 432076e4..11d7ba3a 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetReactor.swift @@ -45,7 +45,7 @@ public final class ItemFilterBottomSheetReactor: Reactor { @Pulse var armorScrolls: [String] = [] @Pulse var etcScrolls: [String] = [] var etcItems: [String] = ["마스터리북", "스킬북", "소비", "설치", "이동수단"] - var selectedScrollIndexes: Int? = nil + var selectedScrollIndexes: Int? var selectedItemIndexes: [IndexPath] = [] var levelRange: (low: Int, high: Int) = (0, 200) @Pulse var route: Route = .none diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift index ad66fa5d..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, String)]) -> Void)? - + override public init() { super.init() } @@ -108,23 +108,23 @@ 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) - + } } @@ -396,7 +396,7 @@ extension ItemFilterBottomSheetViewController { self?.view.endEditing(true) } .disposed(by: disposeBag) - + mainView.clearButton.rx.tap .withUnretained(self) .subscribe(onNext: { owner, _ in @@ -409,16 +409,16 @@ extension ItemFilterBottomSheetViewController { } }) .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, String)] = [] @@ -430,26 +430,26 @@ extension ItemFilterBottomSheetViewController { case .level: 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: // 기존 방식대로 하게 되면 주문서 탭 변경시 각 scrolls가 빈배열 처리되서 오류 - selectedItems.append(("무기주문서",state.originWeaponScrolls[indexPath.row])) + selectedItems.append(("무기주문서", state.originWeaponScrolls[indexPath.row])) case .armorsScrolls: - selectedItems.append(("방어구주문서",state.originArmorScrolls[indexPath.row])) + selectedItems.append(("방어구주문서", state.originArmorScrolls[indexPath.row])) case .etcScrolls: - selectedItems.append(("기타주문서",state.originEtcScrolls[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) @@ -477,7 +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 { diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift index 0bcff79a..48195a79 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeatureDemo/AppDelegate.swift @@ -28,11 +28,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { registerDependencies() return true } - + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {} } @@ -43,7 +43,7 @@ private extension AppDelegate { registerUseCase() registerFactory() } - + func registerProvider() { DIContainer.register(type: NetworkProvider.self) { NetworkProviderImpl() @@ -55,7 +55,7 @@ private extension AppDelegate { AppleLoginProviderMock() } } - + func registerRepository() { DIContainer.register(type: AuthAPIRepository.self) { AuthAPIRepositoryMock(provider: DIContainer.resolve(type: NetworkProvider.self)) @@ -72,12 +72,12 @@ 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() } } - + func registerUseCase() { DIContainer.register(type: FetchSocialCredentialUseCase.self, name: "kakao") { let provider = DIContainer.resolve(type: SocialAuthenticatableProvider.self, name: "kakao") 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 }