From 68ffc50a559994d437d3d3691f397fdae2362910 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 23 Nov 2025 14:18:05 +0000 Subject: [PATCH 01/10] style/#269: Apply SwiftLint autocorrect --- MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift | 2 +- MLS/MLS/Application/AppDelegate.swift | 5 ++--- .../CollectionDetail/CollectionDetailFactoryImpl.swift | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift index 1f4e98c2..2db4535a 100644 --- a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift @@ -10,7 +10,7 @@ public enum CollectionEndPoint { public static func createCollectionList(body: Encodable) -> EndPoint { .init(baseURL: base, path: "/api/v1/collections", method: .POST, body: body) } - + public static func fetchCollection(id: Int) -> ResponsableEndPoint<[BookmarkDTO]> { .init( baseURL: base, diff --git a/MLS/MLS/Application/AppDelegate.swift b/MLS/MLS/Application/AppDelegate.swift index 065d02a5..8ac0be6d 100644 --- a/MLS/MLS/Application/AppDelegate.swift +++ b/MLS/MLS/Application/AppDelegate.swift @@ -18,9 +18,9 @@ import Firebase import KakaoSDKCommon import MyPageFeature import MyPageFeatureInterface +import os import UIKit import UserNotifications -import os @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -327,8 +327,7 @@ extension AppDelegate { repository: DIContainer.resolve( type: DictionaryDetailAPIRepository.self)) } - DIContainer.register(type: FetchDictionaryDetailMonsterMapUseCase.self) - { + DIContainer.register(type: FetchDictionaryDetailMonsterMapUseCase.self) { FetchDictionaryDetailMonsterMapUseCaseImpl( repository: DIContainer.resolve( type: DictionaryDetailAPIRepository.self)) diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift index 4d9c1ef2..31e388de 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift @@ -9,7 +9,7 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { private let addCollectionFactory: AddCollectionFactory private let collectionEditFactory: CollectionEditFactory private let dictionaryDetailFactory: DictionaryDetailFactory - + private let setBookmarkUseCase: SetBookmarkUseCase private let fetchCollectionUseCase: FetchCollectionUseCase From f3c6a7a2130f8fc48b00a5e4d307ae5d40e7343d Mon Sep 17 00:00:00 2001 From: p2glet Date: Tue, 25 Nov 2025 17:14:32 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat/#269:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=EB=A5=BC=20=EC=97=AC=EB=9F=AC=20=EC=BB=AC=EB=A0=89=EC=85=98?= =?UTF-8?q?=EC=97=90=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MLS/.swiftlint.yml | 4 + .../Endpoints/CollectionEndPoint.swift | 18 ++++ .../CollectionAPIRepositoryImpl.swift | 19 ++++ .../AddBookmarksToCollectionUseCaseImpl.swift | 15 +++ .../AddCollectionsToBookmarkUseCaseImpl.swift | 15 +++ .../Repository/CollectionAPIRepository.swift | 4 + .../AddBookmarksToCollectionUseCase.swift | 5 + .../AddCollectionsToBookmarkUseCase.swift | 5 + MLS/MLS/Application/AppCoordinator.swift | 3 +- MLS/MLS/Application/AppDelegate.swift | 17 ++-- .../BookmarkModalFactoryImpl.swift | 10 +- .../BookmarkModal/BookmarkModalReactor.swift | 21 ++++- .../BookmarkModal/BookmarkModalView.swift | 2 +- .../BookmarkModalViewController.swift | 23 ++++- .../CollectionDetailFactoryImpl.swift | 21 ++++- .../CollectionDetailReactor.swift | 10 +- .../CollectionDetailView.swift | 2 +- .../CollectionDetailViewController.swift | 92 ++++++++----------- .../CollectionEditViewController.swift | 3 +- .../CollectionListViewController.swift | 7 +- .../BookmarkModalFactory.swift | 2 +- .../CollectionDetailFactory.swift | 2 +- .../DictionaryDetailBaseViewController.swift | 45 ++++----- .../ItemDictionaryDetailViewController.swift | 3 +- .../MapDictionaryDetailViewController.swift | 3 +- ...onsterDictionaryDetailViewController.swift | 3 +- .../NpcDictionaryDetailViewController.swift | 3 +- .../QuestDictionaryDetailViewController.swift | 3 +- .../DictionaryListViewController.swift | 39 ++++---- 29 files changed, 274 insertions(+), 125 deletions(-) create mode 100644 MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift create mode 100644 MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift create mode 100644 MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift create mode 100644 MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift diff --git a/MLS/.swiftlint.yml b/MLS/.swiftlint.yml index 1ae09fc6..654df49f 100644 --- a/MLS/.swiftlint.yml +++ b/MLS/.swiftlint.yml @@ -13,3 +13,7 @@ function_body_length: disabled_rules: - force_cast - blanket_disable_command + +function_parameter_count: + warning: 5 + error: 10 diff --git a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift index 2db4535a..df6a41af 100644 --- a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift @@ -18,4 +18,22 @@ public enum CollectionEndPoint { method: .GET ) } + + public static func addBookmarksToCollection(id: Int, body: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { + .init( + baseURL: base, + path: "/api/v1/collections/\(id)/bookmarks", + method: .POST, + body: body + ) + } + + public static func addCollectionsToBookmark(id: Int, body: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { + .init( + baseURL: base, + path: "/api/v1/bookmarks/\(id)/collections", + method: .POST, + body: body + ) + } } diff --git a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift index 4332421c..d2d996d3 100644 --- a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift +++ b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift @@ -30,10 +30,29 @@ public class CollectionAPIRepositoryImpl: CollectionAPIRepository { return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) .map { $0.toDomain() } } + + public func addBookmarksToCollection(collectionId: Int, bookmarkIds: [Int]) -> Completable { + let endPoint = CollectionEndPoint.addBookmarksToCollection(id: collectionId, body: AddBookmarkRequestBody(bookmarkIds: bookmarkIds)) + return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) + } + + public func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable { + let endPoint = CollectionEndPoint.addCollectionsToBookmark(id: bookmarkId, body: AddCollectionRequestBody(collectionIds: collectionIds)) + return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) + } + } private extension CollectionAPIRepositoryImpl { struct CreateCollectionRequestDTO: Encodable { let name: String } + + struct AddBookmarkRequestBody: Encodable { + let bookmarkIds: [Int] + } + + struct AddCollectionRequestBody: Encodable { + let collectionIds: [Int] + } } diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift new file mode 100644 index 00000000..b5e1c62a --- /dev/null +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift @@ -0,0 +1,15 @@ +import DomainInterface + +import RxSwift + +public final class AddBookmarksToCollectionUseCaseImpl: AddBookmarksToCollectionUseCase { + private let repository: CollectionAPIRepository + + public init(repository: CollectionAPIRepository) { + self.repository = repository + } + + public func execute(collectionId: Int, bookmarkIds: [Int]) -> Completable { + return repository.addBookmarksToCollection(collectionId: collectionId, bookmarkIds: bookmarkIds) + } +} diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift new file mode 100644 index 00000000..02f97bb5 --- /dev/null +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift @@ -0,0 +1,15 @@ +import DomainInterface + +import RxSwift + +public final class AddCollectionsToBookmarkUseCaseImpl: AddCollectionsToBookmarkUseCase { + private let repository: CollectionAPIRepository + + public init(repository: CollectionAPIRepository) { + self.repository = repository + } + + public func execute(bookmarkId: Int, collectionIds: [Int]) -> Completable { + return repository.addCollectionsToBookmark(bookmarkId: bookmarkId, collectionIds: collectionIds) + } +} diff --git a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift index 8c80c87c..533b9821 100644 --- a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift +++ b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift @@ -9,4 +9,8 @@ public protocol CollectionAPIRepository { func createCollectionList(name: String) -> Completable // 컬렉션 상세 조회 func fetchCollectionUseCase(id: Int) -> Observable<[BookmarkResponse]> + + func addBookmarksToCollection(collectionId: Int, bookmarkIds: [Int]) -> Completable + + func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable } diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift new file mode 100644 index 00000000..e2ecb8d2 --- /dev/null +++ b/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift @@ -0,0 +1,5 @@ +import RxSwift + +public protocol AddBookmarksToCollectionUseCase { + func execute(collectionId: Int, bookmarkIds: [Int]) -> Completable +} diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift new file mode 100644 index 00000000..1a63fe31 --- /dev/null +++ b/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift @@ -0,0 +1,5 @@ +import RxSwift + +public protocol AddCollectionsToBookmarkUseCase { + func execute(bookmarkId: Int, collectionIds: [Int]) -> Completable +} diff --git a/MLS/MLS/Application/AppCoordinator.swift b/MLS/MLS/Application/AppCoordinator.swift index 520823cf..1877aed6 100644 --- a/MLS/MLS/Application/AppCoordinator.swift +++ b/MLS/MLS/Application/AppCoordinator.swift @@ -1,3 +1,5 @@ +import UIKit + import AuthFeature import AuthFeatureInterface import BaseFeature @@ -5,7 +7,6 @@ import BookmarkFeatureInterface import DesignSystem import DictionaryFeatureInterface import MyPageFeatureInterface -import UIKit import RxSwift diff --git a/MLS/MLS/Application/AppDelegate.swift b/MLS/MLS/Application/AppDelegate.swift index 8ac0be6d..3372345a 100644 --- a/MLS/MLS/Application/AppDelegate.swift +++ b/MLS/MLS/Application/AppDelegate.swift @@ -1,6 +1,10 @@ // swiftlint:disable function_body_length // swiftlint:disable line_length +import os +import UIKit +import UserNotifications + import AuthFeature import AuthFeatureInterface import BaseFeature @@ -14,13 +18,11 @@ import DictionaryFeature import DictionaryFeatureInterface import Domain import DomainInterface -import Firebase -import KakaoSDKCommon import MyPageFeature import MyPageFeatureInterface -import os -import UIKit -import UserNotifications + +import Firebase +import KakaoSDKCommon @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -509,6 +511,9 @@ extension AppDelegate { DIContainer.register(type: FetchCollectionUseCase.self) { FetchCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) } + DIContainer.register(type: AddCollectionsToBookmarkUseCase.self) { + AddCollectionsToBookmarkUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + } } fileprivate func registerFactory() { @@ -527,7 +532,7 @@ extension AppDelegate { DIContainer.register(type: BookmarkModalFactory.self) { BookmarkModalFactoryImpl( addCollectionFactory: DIContainer.resolve( - type: AddCollectionFactory.self)) + type: AddCollectionFactory.self), fetchCollectionListUseCase: DIContainer.resolve(type: FetchCollectionListUseCase.self), addCollectionsToBookmarkUseCase: DIContainer.resolve(type: AddCollectionsToBookmarkUseCase.self)) } DIContainer.register(type: LoginFactory.self) { LoginFactoryImpl( diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift index c03c2981..3ba23963 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift @@ -4,13 +4,17 @@ import DomainInterface public final class BookmarkModalFactoryImpl: BookmarkModalFactory { private let addCollectionFactory: AddCollectionFactory + private let fetchCollectionListUseCase: FetchCollectionListUseCase + private let addCollectionsToBookmarkUseCase: AddCollectionsToBookmarkUseCase - public init(addCollectionFactory: AddCollectionFactory) { + public init(addCollectionFactory: AddCollectionFactory, fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionsToBookmarkUseCase: AddCollectionsToBookmarkUseCase) { self.addCollectionFactory = addCollectionFactory + self.fetchCollectionListUseCase = fetchCollectionListUseCase + self.addCollectionsToBookmarkUseCase = addCollectionsToBookmarkUseCase } - public func make(onDismissWithColletions: (([CollectionResponse?]) -> Void)?, onDismissWithMessage: ((CollectionResponse?) -> Void)?) -> BaseViewController { - let reactor = BookmarkModalReactor() + public func make(bookmarkId: Int, onDismissWithColletions: (([CollectionResponse?]) -> Void)?, onDismissWithMessage: ((CollectionResponse?) -> Void)?) -> BaseViewController { + let reactor = BookmarkModalReactor(bookmarkId: bookmarkId, fetchCollectionListUseCase: fetchCollectionListUseCase, addCollectionsToBookmarkUseCase: addCollectionsToBookmarkUseCase) let viewController = BookmarkModalViewController(addCollectionFactory: addCollectionFactory) viewController.reactor = reactor viewController.onDismissWithMessage = onDismissWithMessage diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift index 9a39d87c..3e1f84c7 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift @@ -12,17 +12,21 @@ public final class BookmarkModalReactor: Reactor { public enum Action { case backButtonTapped + case addButtonTapped case addCollectionTapped case selectItem(Int) + case viewWillAppear } public enum Mutation { case toNavigate(Route) case checkCollection([CollectionResponse]) + case setCollection([CollectionResponse]) } public struct State { @Pulse var route: Route + var bookmarkId: Int var collections = [CollectionResponse]() var selectedItems = [CollectionResponse]() } @@ -31,12 +35,23 @@ public final class BookmarkModalReactor: Reactor { private let disposeBag = DisposeBag() - public init() { - self.initialState = State(route: .none) + private let fetchCollectionListUseCase: FetchCollectionListUseCase + private let addCollectionsToBookmarkUseCase: AddCollectionsToBookmarkUseCase + + public init(bookmarkId: Int, fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionsToBookmarkUseCase: AddCollectionsToBookmarkUseCase) { + self.initialState = State(route: .none, bookmarkId: bookmarkId) + self.fetchCollectionListUseCase = fetchCollectionListUseCase + self.addCollectionsToBookmarkUseCase = addCollectionsToBookmarkUseCase } public func mutate(action: Action) -> Observable { switch action { + case .viewWillAppear: + return fetchCollectionListUseCase.execute() + .map { .setCollection($0) } + case .addButtonTapped: + return addCollectionsToBookmarkUseCase.execute(bookmarkId: currentState.bookmarkId, collectionIds: currentState.selectedItems.map { $0.collectionId }) + .andThen(.just(.toNavigate(.dismiss))) case .backButtonTapped: return .just(.toNavigate(.dismiss)) case .addCollectionTapped: @@ -59,6 +74,8 @@ public final class BookmarkModalReactor: Reactor { newState.route = route case .checkCollection(let collections): newState.selectedItems = collections + case .setCollection(let collections): + newState.collections = collections } return newState } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalView.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalView.swift index 3555e832..428e9326 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalView.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalView.swift @@ -36,7 +36,7 @@ final class BookmarkModalView: UIView { private let divider = DividerView() - private let addButtton = CommonButton(style: .normal, title: "", disabledTitle: "추가하기") + public let addButtton = CommonButton(style: .normal, title: "", disabledTitle: "추가하기") // MARK: - Init init() { diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift index 8f13e823..4c3c38f4 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift @@ -80,10 +80,20 @@ extension BookmarkModalViewController { } private func bindUserActions(reactor: Reactor) { + rx.viewWillAppear + .map { .viewWillAppear } + .bind(to: reactor.action) + .disposed(by: disposeBag) + mainView.backButton.rx.tap .map { Reactor.Action.backButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) + + mainView.addButtton.rx.tap + .map { Reactor.Action.addButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) } private func bindViewState(reactor: Reactor) { @@ -91,9 +101,17 @@ extension BookmarkModalViewController { .map(\.collections) .observe(on: MainScheduler.instance) .withUnretained(self) + .bind(onNext: { owner, _ in + owner.mainView.folderCollectionView.reloadData() + }) + .disposed(by: disposeBag) + + reactor.state + .map(\.selectedItems) + .observe(on: MainScheduler.instance) + .withUnretained(self) .bind(onNext: { owner, items in owner.mainView.setButtonTitle(count: items.count) - owner.mainView.folderCollectionView.reloadData() }) .disposed(by: disposeBag) @@ -101,12 +119,13 @@ extension BookmarkModalViewController { .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } .withUnretained(self) + .observe(on: MainScheduler.instance) .subscribe(onNext: { owner, route in switch route { case .dismiss: owner.dismiss(animated: true) case .addCollection: - let viewController = owner.addCollectionFactory.make(collection: nil, onDismissWithMessage: { [weak self] collection in + let viewController = owner.addCollectionFactory.make( collection: nil, onDismissWithMessage: { [weak self] collection in self?.onDismissWithMessage?(collection) }) owner.present(viewController, animated: true) diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift index 31e388de..282ff157 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift @@ -1,8 +1,11 @@ import BaseFeature import BookmarkFeatureInterface +import DesignSystem import DictionaryFeatureInterface import DomainInterface +import RxSwift + public final class CollectionDetailFactoryImpl: CollectionDetailFactory { private let bookmarkModalFactory: BookmarkModalFactory private let collectionSettingFactory: CollectionSettingFactory @@ -31,12 +34,13 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { self.fetchCollectionUseCase = fetchCollectionUseCase } - public func make(collection: CollectionResponse) -> BaseViewController { + public func make(collection: CollectionResponse, onMoveToMain: (() -> Void)?) -> BaseViewController { let reactor = CollectionDetailReactor( setBookmarkUseCase: setBookmarkUseCase, fetchCollectionUseCase: fetchCollectionUseCase, collection: collection ) + let viewController = CollectionDetailViewController( reactor: reactor, bookmarkModalFactory: bookmarkModalFactory, @@ -45,6 +49,21 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { collectionEditFactory: collectionEditFactory, dictionaryDetailFactory: dictionaryDetailFactory ) + + reactor.pulse(\.$route) + .compactMap { $0 } + .observe(on: MainScheduler.instance) + .bind(onNext: { [weak viewController] route in + switch route { + case .toMain: + onMoveToMain?() + viewController?.navigationController?.popToRootViewController(animated: true) + default: + break + } + }) + .disposed(by: viewController.disposeBag) + return viewController } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift index 01c15359..a40f58b7 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift @@ -41,7 +41,6 @@ public final class CollectionDetailReactor: Reactor { } let type = DictionaryMainViewType.bookmark var collection: CollectionResponse - var bookmarks = [BookmarkResponse]() var lastDeletedBookmark: BookmarkResponse? } @@ -62,7 +61,7 @@ public final class CollectionDetailReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .toggleBookmark(let id, let isSelected): - guard let bookmarkItem = currentState.bookmarks.first(where: { $0.originalId == id }) else { return .empty() } + guard let bookmarkItem = currentState.collection.recentBookmarks.first(where: { $0.originalId == id }) else { return .empty() } let saveDeletedMutation: Observable = isSelected ? .just(.setLastDeletedBookmark(bookmarkItem)) @@ -84,8 +83,7 @@ public final class CollectionDetailReactor: Reactor { return fetchCollectionUseCase.execute(id: currentState.collection.collectionId) .map { .setItems($0) } case .addButtonTapped: - // 컬렉션 추가 - return .empty() + return .just(.navigateTo(.toMain)) case .bookmarkButtonTapped: return .just(.navigateTo(.toMain)) case .selectSetting(let menu): @@ -105,7 +103,7 @@ public final class CollectionDetailReactor: Reactor { ]) ) case .dataTapped(let index): - let item = currentState.bookmarks[index] + let item = currentState.collection.recentBookmarks[index] guard let type = item.type.toDictionaryType else { return .empty() } return .just(.navigateTo(.detail(type, item.originalId))) } @@ -115,7 +113,7 @@ public final class CollectionDetailReactor: Reactor { var newState = state switch mutation { case .setItems(let items): - newState.bookmarks = items + newState.collection.recentBookmarks = items case .navigateTo(let route): newState.route = route case .setMenu(let menu): diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift index 8e5c532a..aa676d30 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift @@ -76,7 +76,7 @@ private extension CollectionDetailView { } emptyView.snp.makeConstraints { make in - make.center.equalToSuperview() + make.edges.equalToSuperview() } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift index 1b740217..e1c01b0a 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift @@ -27,7 +27,14 @@ public final class CollectionDetailViewController: BaseViewController, View { // MARK: - Components private var mainView: CollectionDetailView - public init(reactor: CollectionDetailReactor, bookmarkModalFactory: BookmarkModalFactory, collectionSettingFactory: CollectionSettingFactory, addCollectionFactory: AddCollectionFactory, collectionEditFactory: CollectionEditFactory, dictionaryDetailFactory: DictionaryDetailFactory) { + public init( + reactor: CollectionDetailReactor, + bookmarkModalFactory: BookmarkModalFactory, + collectionSettingFactory: CollectionSettingFactory, + addCollectionFactory: AddCollectionFactory, + collectionEditFactory: CollectionEditFactory, + dictionaryDetailFactory: DictionaryDetailFactory + ) { self.mainView = CollectionDetailView(navTitle: reactor.currentState.collection.name) self.bookmarkModalFactory = bookmarkModalFactory self.collectionSettingFactory = collectionSettingFactory @@ -164,8 +171,6 @@ extension CollectionDetailViewController { .withUnretained(self) .subscribe { owner, route in switch route { - case .toMain: - owner.tabBarController?.selectedIndex = 0 case .dismiss: owner.navigationController?.popViewController(animated: true) case .edit: @@ -200,58 +205,35 @@ extension CollectionDetailViewController: UICollectionViewDelegate, UICollection else { return UICollectionViewCell() } - -// cell.inject( -// type: .bookmark, -// input: DictionaryListCell.Input( -// type: item.type, -// mainText: item.mainText, -// subText: item.subText, -// image: item.image, -// isSelected: item.isBookmarked -// ), -// onIconTapped: { [weak self] isSelcted in -// guard let self = self else { return } -// if item.isBookmarked { -// self.reactor?.action.onNext(.toggleBookmark(item.id, isSelcted)) -// } else { -// // 로그인 여부 확인 -// if false { -// GuideAlertFactory.show( -// mainText: "북마크를 하려면 로그인이 필요해요.", -// ctaText: "로그인 하기", -// cancelText: "취소", -// ctaAction: { -// print("로그인 화면으로 이동") -// }, -// cancelAction: { -// print("취소됨") -// } -// ) -// } else { -// self.reactor?.action.onNext(.toggleBookmark(item.id, isSelcted)) -// SnackBarFactory.createSnackBar(type: .normal, image: item.image, imageBackgroundColor: item.type.backgroundColor, text: "아이템을 북마크에 추가했어요.", buttonText: "컬렉션 추가", buttonAction: { -// DispatchQueue.main.async { -// let viewController = self.bookmarkModalFactory.make(onDismissWithColletions: { _ in }, onDismissWithMessage: { _ in -// ToastFactory.createToast(message: "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요.") -// }) -// -// viewController.modalPresentationStyle = .pageSheet -// -// if let sheet = viewController.sheetPresentationController { -// sheet.detents = [.medium(), .large()] -// sheet.prefersGrabberVisible = true -// sheet.preferredCornerRadius = 16 -// } -// -// self.present(viewController, animated: true) -// } -// }) -// } -// } -// } -// ) - + var subText: String? { + [.item, .monster, .quest].contains(item.type) ? item.level.map { "Lv. \($0)" } : nil + } + cell.inject( + type: .bookmark, + input: DictionaryListCell.Input( + type: item.type, + mainText: item.name, + subText: subText, + imageUrl: item.imageUrl ?? "", + isBookmarked: true + ), + onBookmarkTapped: { [weak self] isSelected in + guard let self = self else { return } + + self.reactor?.action.onNext(.toggleBookmark(item.originalId, isSelected)) + + SnackBarFactory.createSnackBar( + type: .delete, + imageUrl: item.imageUrl, + imageBackgroundColor: item.type.backgroundColor, + text: "아이템을 북마크에서 삭제했어요.", + buttonText: "되돌리기", + buttonAction: { [weak self] in + self?.reactor?.action.onNext(.undoLastDeletedBookmark) + } + ) + } + ) return cell } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift index f11a1710..471e06a2 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift @@ -124,7 +124,8 @@ extension CollectionEditViewController { case .dismiss: owner.navigationController?.popViewController(animated: true) case .collcectionList: - let viewController = owner.bookmarkModalFactory.make(onDismissWithColletions: { _ in + // 수정필요 bookmarkId -> bookmarkIds + let viewController = owner.bookmarkModalFactory.make(bookmarkId: reactor.currentState.selectedCollections.first?.collectionId ?? 0, onDismissWithColletions: { _ in }, onDismissWithMessage: { _ in diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift index 7269b4f2..56926363 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift @@ -2,6 +2,7 @@ import UIKit import BaseFeature import BookmarkFeatureInterface +import DesignSystem import DomainInterface import ReactorKit @@ -104,7 +105,11 @@ extension CollectionListViewController { .subscribe { owner, route in switch route { case .detail(let collection): - let viewController = owner.detailFactory.make(collection: collection) + let viewController = owner.detailFactory.make(collection: collection, onMoveToMain: { + if let tabBarController = owner.tabBarController as? BottomTabBarController { + tabBarController.selectTab(index: 0) + } + }) owner.tabBarController?.navigationController?.pushViewController(viewController, animated: true) default: break diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift index acea1e83..46866daf 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift @@ -2,5 +2,5 @@ import BaseFeature import DomainInterface public protocol BookmarkModalFactory { - func make(onDismissWithColletions: (([CollectionResponse?]) -> Void)?, onDismissWithMessage: ((CollectionResponse?) -> Void)?) -> BaseViewController + func make(bookmarkId: Int, onDismissWithColletions: (([CollectionResponse?]) -> Void)?, onDismissWithMessage: ((CollectionResponse?) -> Void)?) -> BaseViewController } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionDetailFactory.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionDetailFactory.swift index ca409fc1..67d08357 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionDetailFactory.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionDetailFactory.swift @@ -2,5 +2,5 @@ import BaseFeature import DomainInterface public protocol CollectionDetailFactory { - func make(collection: CollectionResponse) -> BaseViewController + func make(collection: CollectionResponse, onMoveToMain: (() -> Void)?) -> BaseViewController } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift index 4e7fd8f4..ec0b5020 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift @@ -254,21 +254,22 @@ extension DictionaryDetailBaseViewController { backgroundColor: UIColor, isBookmarked: @escaping (T) -> Bool, toggleBookmark: @escaping (Bool) -> Void, - undoLastDeleted: @escaping () -> Void) -> Disposable { + undoLastDeleted: @escaping () -> Void, + bookmarkId: Observable + ) -> Disposable { buttonTap - .withLatestFrom(currentItem) + .withLatestFrom(Observable.combineLatest(currentItem, bookmarkId)) .observe(on: MainScheduler.instance) - .bind { [weak self] item in + .bind { [weak self] item, bookmarkId in guard let self else { return } - guard isLogin() else { GuideAlertFactory.show( mainText: "북마크를 하려면 로그인이 필요해요.", ctaText: "로그인 하기", cancelText: "취소", ctaAction: { - let viewController = self.loginFactory.make(exitRoute: .pop) - self.navigationController?.pushViewController(viewController, animated: true) + let vc = self.loginFactory.make(exitRoute: .pop) + self.navigationController?.pushViewController(vc, animated: true) }, cancelAction: nil ) @@ -294,24 +295,24 @@ extension DictionaryDetailBaseViewController { text: "아이템을 북마크에 추가했어요.", buttonText: "컬렉션 추가", buttonAction: { [weak self] in - guard let self else { return } - DispatchQueue.main.async { - let viewController = self.bookmarkModalFactory.make( - onDismissWithColletions: { _ in }, - onDismissWithMessage: { _ in - ToastFactory.createToast( - message: "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." - ) - } - ) - viewController.modalPresentationStyle = .pageSheet - if let sheet = viewController.sheetPresentationController { - sheet.detents = [.medium(), .large()] - sheet.prefersGrabberVisible = true - sheet.preferredCornerRadius = 16 + guard let self, + let id = bookmarkId else { return } + let viewController = self.bookmarkModalFactory.make( + bookmarkId: id, + onDismissWithColletions: { _ in }, + onDismissWithMessage: { _ in + ToastFactory.createToast( + message: "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." + ) } - self.present(viewController, animated: true) + ) + viewController.modalPresentationStyle = .pageSheet + if let sheet = viewController.sheetPresentationController { + sheet.detents = [.medium(), .large()] + sheet.prefersGrabberVisible = true + sheet.preferredCornerRadius = 16 } + self.present(viewController, animated: true) } ) } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift index 49ed775a..30936979 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift @@ -221,7 +221,8 @@ extension ItemDictionaryDetailViewController { backgroundColor: type.backgroundColor, isBookmarked: { $0.bookmarkId != nil }, toggleBookmark: { isDeleting in reactor.action.onNext(.toggleBookmark(isDeleting)) }, - undoLastDeleted: { reactor.action.onNext(.undoLastDeletedBookmark) } + undoLastDeleted: { reactor.action.onNext(.undoLastDeletedBookmark) }, + bookmarkId: reactor.state.map(\.itemDetailInfo.bookmarkId) ) .disposed(by: disposeBag) } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift index 69e14300..65bda22b 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift @@ -158,7 +158,8 @@ extension MapDictionaryDetailViewController { backgroundColor: type.backgroundColor, isBookmarked: { $0.bookmarkId != nil }, toggleBookmark: { isDeleting in reactor.action.onNext(.toggleBookmark(isDeleting)) }, - undoLastDeleted: { reactor.action.onNext(.undoLastDeletedBookmark) } + undoLastDeleted: { reactor.action.onNext(.undoLastDeletedBookmark) }, + bookmarkId: reactor.state.map(\.mapDetailInfo.bookmarkId) ) .disposed(by: disposeBag) } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift index 4c655911..d66f6d37 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift @@ -203,7 +203,8 @@ extension MonsterDictionaryDetailViewController { backgroundColor: type.backgroundColor, isBookmarked: { $0.bookmarkId != nil }, toggleBookmark: { isDeleting in reactor.action.onNext(.toggleBookmark(isDeleting)) }, - undoLastDeleted: { reactor.action.onNext(.undoLastDeletedBookmark) } + undoLastDeleted: { reactor.action.onNext(.undoLastDeletedBookmark) }, + bookmarkId: reactor.state.map(\.monsterDetailInfo.bookmarkId) ) .disposed(by: disposeBag) } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift index 2b3a8619..9b64f6ce 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift @@ -156,7 +156,8 @@ extension NpcDictionaryDetailViewController { backgroundColor: type.backgroundColor, isBookmarked: { $0.bookmarkId != nil }, toggleBookmark: { isDeleting in reactor.action.onNext(.toggleBookmark(isDeleting)) }, - undoLastDeleted: { reactor.action.onNext(.undoLastDeletedBookmark) } + undoLastDeleted: { reactor.action.onNext(.undoLastDeletedBookmark) }, + bookmarkId: reactor.state.map(\.npcDetailInfo.bookmarkId) ) .disposed(by: disposeBag) } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift index 195c9b83..1caf7486 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift @@ -158,7 +158,8 @@ extension QuestDictionaryDetailViewController { backgroundColor: type.backgroundColor, isBookmarked: { $0.bookmarkId != nil }, toggleBookmark: { isDeleting in reactor.action.onNext(.toggleBookmark(isDeleting)) }, - undoLastDeleted: { reactor.action.onNext(.undoLastDeletedBookmark) } + undoLastDeleted: { reactor.action.onNext(.undoLastDeletedBookmark) }, + bookmarkId: reactor.state.map(\.detailInfo.bookmarkId) ) .disposed(by: disposeBag) } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift index 069ac574..8b73c5c1 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift @@ -58,40 +58,41 @@ public final class DictionaryListViewController: BaseViewController, View { addViews() setupConstraints() configureUI() - } } // MARK: - SetUp -extension DictionaryListViewController { - fileprivate func addViews() { +private extension DictionaryListViewController { + func addViews() { view.addSubview(mainView) } - fileprivate func setupConstraints() { + func setupConstraints() { mainView.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide) make.horizontalEdges.bottom.equalToSuperview() } } - fileprivate func configureUI() { + func configureUI() { mainView.listCollectionView.collectionViewLayout = createListLayout() mainView.listCollectionView.delegate = self mainView.listCollectionView.dataSource = self mainView.listCollectionView.register( DictionaryListCell.self, - forCellWithReuseIdentifier: DictionaryListCell.identifier) + forCellWithReuseIdentifier: DictionaryListCell.identifier + ) } - fileprivate func createListLayout() -> UICollectionViewLayout { + func createListLayout() -> UICollectionViewLayout { let layoutFactory = LayoutFactory() let layout = CompositionalLayoutBuilder() .section { _ in layoutFactory.getDictionaryListLayout() } .build() layout.register( Neutral300DividerView.self, - forDecorationViewOfKind: Neutral300DividerView.identifier) + forDecorationViewOfKind: Neutral300DividerView.identifier + ) return layout } } @@ -116,7 +117,6 @@ extension DictionaryListViewController { } func bindViewState(reactor: Reactor) { - reactor.state .map { $0.totalCounts } .distinctUntilChanged() @@ -201,7 +201,8 @@ extension DictionaryListViewController { // MARK: - Delegate extension DictionaryListViewController: UICollectionViewDelegate, - UICollectionViewDataSource { + UICollectionViewDataSource +{ public func collectionView( _ collectionView: UICollectionView, numberOfItemsInSection section: Int ) -> Int { @@ -251,7 +252,8 @@ extension DictionaryListViewController: UICollectionViewDelegate, let viewController = self.loginFactory.make( exitRoute: .pop) self.navigationController?.pushViewController( - viewController, animated: true) + viewController, animated: true + ) }, cancelAction: nil ) @@ -283,20 +285,24 @@ extension DictionaryListViewController: UICollectionViewDelegate, buttonText: "컬렉션 추가", buttonAction: { DispatchQueue.main.async { + guard let reactor = self.reactor, + let id = reactor.currentState.listItems[indexPath.row].bookmarkId else { return } let viewController = self.bookmarkModalFactory .make( + bookmarkId: id, onDismissWithColletions: { _ in }, onDismissWithMessage: { _ in ToastFactory.createToast( message: - "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." + "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." ) } ) viewController.modalPresentationStyle = .pageSheet if let sheet = viewController - .sheetPresentationController { + .sheetPresentationController + { sheet.detents = [.medium(), .large()] sheet.prefersGrabberVisible = true sheet.preferredCornerRadius = 16 @@ -328,7 +334,8 @@ extension DictionaryListViewController: UICollectionViewDelegate, default: // 단일 타입일 경우 리액터 타입에 따라 처리 viewController = detailFactory.make( - type: reactor.currentState.type, id: item.id) + type: reactor.currentState.type, id: item.id + ) } navigationController?.pushViewController(viewController, animated: true) } @@ -339,8 +346,8 @@ extension DictionaryListViewController: UICollectionViewDelegate, let height = scrollView.frame.size.height if offsetY > contentHeight - height - 100 { - reactor?.action.onNext(.setCurrentPage) // 페이지 올리고 - reactor?.action.onNext(.fetchList) // 해당 페이지로 데이터 불러오기 + reactor?.action.onNext(.setCurrentPage) // 페이지 올리고 + reactor?.action.onNext(.fetchList) // 해당 페이지로 데이터 불러오기 } } } From 75d7960b7e22d40a5d36da4d4d9de9c2f866c402 Mon Sep 17 00:00:00 2001 From: p2glet Date: Tue, 25 Nov 2025 17:56:47 +0900 Subject: [PATCH 03/10] =?UTF-8?q?chore/#269:=20swiftLint=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MLS/.swiftlint.yml | 8 ++++ .../DTO/AlarmDTO/AlarmResponseDTO.swift | 24 +++++----- .../Endpoints/CollectionEndPoint.swift | 4 +- .../Endpoints/DictionaryListEndPoint.swift | 11 ++++- .../CollectionAPIRepositoryImpl.swift | 10 ++-- ...eckNotificationPermissionUseCaseImpl.swift | 8 ++-- .../Repository/CollectionAPIRepository.swift | 4 +- MLS/MLS/Application/AppDelegate.swift | 1 - .../BookmarkListViewController.swift | 8 ++-- .../BookmarkOnBoardingView.swift | 14 +++--- .../CollectionDetailViewController.swift | 2 +- .../DictionaryDetailBaseView.swift | 48 +++++++++++++++++-- .../DictionaryDetailBaseViewController.swift | 7 +-- .../DictionaryDetailFactoryImpl.swift | 16 ++++++- .../Map/MapDictionaryDetailReactor.swift | 18 ++++++- .../DictionaryListFactoryImpl.swift | 10 +++- .../DictionaryListReactor.swift | 2 - .../DictionaryListViewController.swift | 11 ++--- .../DictionarySearchResultReactor.swift | 19 ++++---- ...DictionarySearchResultViewController.swift | 6 +-- .../ItemFilterBottomSheetViewController.swift | 2 +- .../CustomerSupportBaseView.swift | 16 ++++++- .../MyPageFeature/Main/MyPageMainCell.swift | 10 ++-- .../Main/MyPageMainFactoryImpl.swift | 17 ++++++- .../NotificationSettingView.swift | 16 ++++++- .../SetProfile/SetProfileFactoryImpl.swift | 9 +++- .../MyPageFeatureDemo/AppDelegate.swift | 1 - 27 files changed, 218 insertions(+), 84 deletions(-) diff --git a/MLS/.swiftlint.yml b/MLS/.swiftlint.yml index 654df49f..6fa49957 100644 --- a/MLS/.swiftlint.yml +++ b/MLS/.swiftlint.yml @@ -17,3 +17,11 @@ disabled_rules: function_parameter_count: warning: 5 error: 10 + +identifier_name: + min_length: 2 + max_length: 40 + +type_name: + min_length: 3 + max_length: 60 diff --git a/MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift b/MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift index 6173f5aa..74145e43 100644 --- a/MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift +++ b/MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift @@ -8,18 +8,6 @@ public struct AlarmResponseDTO: Decodable { case normal(NormalContent) case all(AllContent) - public struct NormalContent: Decodable { - public let type: String - public let title: String - public let link: String - public let date: [Int] - } - - public struct AllContent: Decodable { - public let alrim: NormalContent - public let alreadyRead: Bool - } - public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() @@ -42,6 +30,18 @@ public struct AlarmResponseDTO: Decodable { ) } } + + public struct NormalContent: Decodable { + public let type: String + public let title: String + public let link: String + public let date: [Int] + } + + public struct AllContent: Decodable { + public let alrim: NormalContent + public let alreadyRead: Bool + } } public extension AlarmResponseDTO { diff --git a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift index df6a41af..84ad17fc 100644 --- a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift @@ -18,7 +18,7 @@ public enum CollectionEndPoint { method: .GET ) } - + public static func addBookmarksToCollection(id: Int, body: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { .init( baseURL: base, @@ -27,7 +27,7 @@ public enum CollectionEndPoint { body: body ) } - + public static func addCollectionsToBookmark(id: Int, body: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { .init( baseURL: base, diff --git a/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift b/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift index e6831513..80c81a2d 100644 --- a/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift @@ -34,7 +34,16 @@ 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> { + 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: ",") diff --git a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift index d2d996d3..2a105e2f 100644 --- a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift +++ b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift @@ -30,28 +30,28 @@ public class CollectionAPIRepositoryImpl: CollectionAPIRepository { return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) .map { $0.toDomain() } } - + public func addBookmarksToCollection(collectionId: Int, bookmarkIds: [Int]) -> Completable { let endPoint = CollectionEndPoint.addBookmarksToCollection(id: collectionId, body: AddBookmarkRequestBody(bookmarkIds: bookmarkIds)) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) } - + public func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable { let endPoint = CollectionEndPoint.addCollectionsToBookmark(id: bookmarkId, body: AddCollectionRequestBody(collectionIds: collectionIds)) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) } - + } private extension CollectionAPIRepositoryImpl { struct CreateCollectionRequestDTO: Encodable { let name: String } - + struct AddBookmarkRequestBody: Encodable { let bookmarkIds: [Int] } - + struct AddCollectionRequestBody: Encodable { let collectionIds: [Int] } diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/CheckNotificationPermissionUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/CheckNotificationPermissionUseCaseImpl.swift index 637db274..72deffc0 100644 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/CheckNotificationPermissionUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/CheckNotificationPermissionUseCaseImpl.swift @@ -11,10 +11,10 @@ public final class CheckNotificationPermissionUseCaseImpl: CheckNotificationPerm return Single.create { single in UNUserNotificationCenter.current().getNotificationSettings { settings in switch settings.authorizationStatus { - case .authorized: - single(.success(true)) - default: - single(.success(false)) + case .authorized: + single(.success(true)) + default: + single(.success(false)) } } return Disposables.create() diff --git a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift index 533b9821..16029db8 100644 --- a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift +++ b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift @@ -9,8 +9,8 @@ public protocol CollectionAPIRepository { func createCollectionList(name: String) -> Completable // 컬렉션 상세 조회 func fetchCollectionUseCase(id: Int) -> Observable<[BookmarkResponse]> - + func addBookmarksToCollection(collectionId: Int, bookmarkIds: [Int]) -> Completable - + func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable } diff --git a/MLS/MLS/Application/AppDelegate.swift b/MLS/MLS/Application/AppDelegate.swift index 3372345a..c84a13ce 100644 --- a/MLS/MLS/Application/AppDelegate.swift +++ b/MLS/MLS/Application/AppDelegate.swift @@ -1,5 +1,4 @@ // swiftlint:disable function_body_length -// swiftlint:disable line_length import os import UIKit diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift index ced8efbc..52de21b4 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift @@ -170,11 +170,11 @@ extension BookmarkListViewController { break } case .detail(let type, let id): - let vc = owner.dictionaryDetailFactory.make(type: type, id: id) - owner.navigationController?.pushViewController(vc, animated: true) + let viewcontroller = owner.dictionaryDetailFactory.make(type: type, id: id) + owner.navigationController?.pushViewController(viewcontroller, animated: true) case .login: - let vc = owner.loginFactory.make(exitRoute: .pop) - owner.navigationController?.pushViewController(vc, animated: true) + let viewcontroller = owner.loginFactory.make(exitRoute: .pop) + owner.navigationController?.pushViewController(viewcontroller, animated: true) case .dictionary: if let tabBarController = owner.tabBarController as? BottomTabBarController { diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingView.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingView.swift index a2907d89..219e47b7 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingView.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkOnBoarding/BookmarkOnBoardingView.swift @@ -49,14 +49,14 @@ public final class BookmarkOnBoardingView: UIView { case .end: return .second } } + } - struct OnboardingContent { - let imageName: String - let title: String - let description: String - let isBackButtonHidden: Bool - let buttonTitle: String - } + struct OnboardingContent { + let imageName: String + let title: String + let description: String + let isBackButtonHidden: Bool + let buttonTitle: String } enum Constant { diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift index e1c01b0a..1152b822 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift @@ -219,7 +219,7 @@ extension CollectionDetailViewController: UICollectionViewDelegate, UICollection ), onBookmarkTapped: { [weak self] isSelected in guard let self = self else { return } - + self.reactor?.action.onNext(.toggleBookmark(item.originalId, isSelected)) SnackBarFactory.createSnackBar( diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseView.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseView.swift index f8e4f3ae..cc99f127 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseView.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseView.swift @@ -50,7 +50,21 @@ class DictionaryDetailBaseView: UIView { public let backButton: UIButton = { let button = UIButton() - button.setImage(DesignSystemAsset.image(named: "arrowBack")?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: Constant.iconInset, left: Constant.iconInset, bottom: Constant.iconInset, right: Constant.iconInset)), for: .normal) + button + .setImage( + DesignSystemAsset + .image(named: "arrowBack")? + .withRenderingMode(.alwaysTemplate) + .resizableImage( + withCapInsets: UIEdgeInsets( + top: Constant.iconInset, + left: Constant.iconInset, + bottom: Constant.iconInset, + right: Constant.iconInset + ) + ), + for: .normal + ) button.tintColor = .textColor return button @@ -60,7 +74,21 @@ class DictionaryDetailBaseView: UIView { public let dictButton: UIButton = { let button = UIButton() - button.setImage(DesignSystemAsset.image(named: "dictionary")?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: Constant.iconInset, left: Constant.iconInset, bottom: Constant.iconInset, right: Constant.iconInset)), for: .normal) + button + .setImage( + DesignSystemAsset + .image(named: "dictionary")? + .withRenderingMode(.alwaysTemplate) + .resizableImage( + withCapInsets: UIEdgeInsets( + top: Constant.iconInset, + left: Constant.iconInset, + bottom: Constant.iconInset, + right: Constant.iconInset + ) + ), + for: .normal + ) button.tintColor = .textColor return button @@ -68,7 +96,21 @@ class DictionaryDetailBaseView: UIView { public let reportButton: UIButton = { let button = UIButton() - button.setImage(DesignSystemAsset.image(named: "errorBlack")?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: Constant.iconInset, left: Constant.iconInset, bottom: Constant.iconInset, right: Constant.iconInset)), for: .normal) + button + .setImage( + DesignSystemAsset + .image(named: "errorBlack")? + .withRenderingMode(.alwaysTemplate) + .resizableImage( + withCapInsets: UIEdgeInsets( + top: Constant.iconInset, + left: Constant.iconInset, + bottom: Constant.iconInset, + right: Constant.iconInset + ) + ), + for: .normal + ) button.tintColor = .textColor return button diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift index ec0b5020..78de6569 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift @@ -95,7 +95,8 @@ extension DictionaryDetailBaseViewController: UIScrollViewDelegate { // 탭바의 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 + 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 } else { @@ -268,8 +269,8 @@ extension DictionaryDetailBaseViewController { ctaText: "로그인 하기", cancelText: "취소", ctaAction: { - let vc = self.loginFactory.make(exitRoute: .pop) - self.navigationController?.pushViewController(vc, animated: true) + let viewController = self.loginFactory.make(exitRoute: .pop) + self.navigationController?.pushViewController(viewController, animated: true) }, cancelAction: nil ) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift index 2b672093..e69107a9 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift @@ -71,7 +71,13 @@ public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { break case .item: viewController = ItemDictionaryDetailViewController(type: .item, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory()) - let reactor = ItemDictionaryDetailReactor(dictionaryDetailItemUseCase: dictionaryDetailItemUseCase, dictionaryDetailItemDropMonsterUseCase: dictionaryDetailItemDropMonsterUseCase, checkLoginUseCase: checkLoginUseCase, setBookmarkUseCase: setBookmarkUseCase, id: id) + let reactor = ItemDictionaryDetailReactor( + dictionaryDetailItemUseCase: dictionaryDetailItemUseCase, + dictionaryDetailItemDropMonsterUseCase: dictionaryDetailItemDropMonsterUseCase, + checkLoginUseCase: checkLoginUseCase, + setBookmarkUseCase: setBookmarkUseCase, + id: id + ) if let viewController = viewController as? ItemDictionaryDetailViewController { viewController.reactor = reactor } @@ -116,7 +122,13 @@ public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { } case .quest: viewController = QuestDictionaryDetailViewController(type: .quest, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory()) - let reactor = QuestDictionaryDetailReactor(dictionaryDetailQuestUseCase: dictionaryDetailQuestUseCase, dictionaryDetailQuestLinkedQuestUseCase: dictionaryDetailQuestLinkedQuestsUseCase, checkLoginUseCase: checkLoginUseCase, setBookmarkUseCase: setBookmarkUseCase, id: id) + let reactor = QuestDictionaryDetailReactor( + dictionaryDetailQuestUseCase: dictionaryDetailQuestUseCase, + dictionaryDetailQuestLinkedQuestUseCase: dictionaryDetailQuestLinkedQuestsUseCase, + checkLoginUseCase: checkLoginUseCase, + setBookmarkUseCase: setBookmarkUseCase, + id: id + ) if let viewController = viewController as? QuestDictionaryDetailViewController { viewController.reactor = reactor } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift index 0ed69b92..926fae83 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift @@ -53,7 +53,23 @@ public final class MapDictionaryDetailReactor: Reactor { setBookmarkUseCase: SetBookmarkUseCase, id: Int ) { - initialState = State(mapDetailInfo: DictionaryDetailMapResponse(mapId: nil, nameKr: nil, nameEn: nil, regionName: nil, detailName: nil, topRegionName: nil, mapUrl: nil, iconUrl: nil, bookmarkId: nil), spawnMonsters: [], npcs: [], type: .map, id: id) + initialState = State( + mapDetailInfo: DictionaryDetailMapResponse( + mapId: nil, + nameKr: nil, + nameEn: nil, + regionName: nil, + detailName: nil, + topRegionName: nil, + mapUrl: nil, + iconUrl: nil, + bookmarkId: nil + ), + spawnMonsters: [], + npcs: [], + type: .map, + id: id + ) self.dictionaryDetailMapUseCase = dictionaryDetailMapUseCase self.dictionaryDetailMapSpawnMonsterUseCase = dictionaryDetailMapSpawnMonsterUseCase diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift index 47a37655..02884710 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListFactoryImpl.swift @@ -70,7 +70,15 @@ public final class DictionaryListFactoryImpl: DictionaryMainListFactory { setBookmarkUseCase: setBookmarkUseCase, parseItemFilterResultUseCase: parseItemFilterResultUseCase ) - let viewController = DictionaryListViewController(reactor: reactor, itemFilterFactory: itemFilterFactory, monsterFilterFactory: monsterFilterFactory, sortedFactory: sortedFactory, bookmarkModalFactory: bookmarkModalFactory, detailFactory: detailFactory, loginFactory: loginFactory()) + let viewController = DictionaryListViewController( + reactor: reactor, + itemFilterFactory: itemFilterFactory, + monsterFilterFactory: monsterFilterFactory, + sortedFactory: sortedFactory, + bookmarkModalFactory: bookmarkModalFactory, + detailFactory: detailFactory, + loginFactory: loginFactory() + ) if listType == .search { viewController.isBottomTabbarHidden = true } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift index abc6b8de..e1f8c2c6 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift @@ -1,5 +1,3 @@ -// swiftlint:disable function_body_length - import DomainInterface import ReactorKit import RxSwift diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift index 8b73c5c1..d7e95acc 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift @@ -200,9 +200,7 @@ extension DictionaryListViewController { } // MARK: - Delegate -extension DictionaryListViewController: UICollectionViewDelegate, - UICollectionViewDataSource -{ +extension DictionaryListViewController: UICollectionViewDelegate, UICollectionViewDataSource{ public func collectionView( _ collectionView: UICollectionView, numberOfItemsInSection section: Int ) -> Int { @@ -298,11 +296,8 @@ extension DictionaryListViewController: UICollectionViewDelegate, ) } ) - viewController.modalPresentationStyle = - .pageSheet - if let sheet = viewController - .sheetPresentationController - { + viewController.modalPresentationStyle = .pageSheet + if let sheet = viewController.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersGrabberVisible = true sheet.preferredCornerRadius = 16 diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift index 57614151..f8cf3ca6 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultReactor.swift @@ -64,7 +64,7 @@ public final class DictionarySearchResultReactor: Reactor { } else { return .empty() } - // 검색 결과 화면에서 재검색 시 + // 검색 결과 화면에서 재검색 시 case .searchButtonTapped(let keyword): let keyword = keyword ?? "" @@ -82,7 +82,7 @@ public final class DictionarySearchResultReactor: Reactor { newState.keyword = keyword case .setCounts(let counts): newState.counts = counts - } + } return newState } @@ -90,7 +90,7 @@ public final class DictionarySearchResultReactor: Reactor { public func transform(mutation: Observable) -> Observable { let keywordChanges = mutation .compactMap { mutation -> String? in - if case let .setKeyword(keyword) = mutation { return keyword } + if case .setKeyword(let keyword) = mutation { return keyword } return nil } .distinctUntilChanged() // 중복 keyword 방지 @@ -98,16 +98,15 @@ public final class DictionarySearchResultReactor: Reactor { guard let self = self else { return .empty() } let types = ["search", "monsters", "items", "npcs", "maps", "quests"] let countObservables = types.map { type in - self.dictionarySearchCountUseCase.execute(type: type, keyword: keyword) - .map { $0.count ?? 0 } - } + self.dictionarySearchCountUseCase.execute(type: type, keyword: keyword) + .map { $0.count ?? 0 } + } return Observable.zip(countObservables) - .map { counts in - Mutation.setCounts(counts) - } + .map { counts in + Mutation.setCounts(counts) + } } return Observable.merge(mutation, keywordChanges) } - } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift index fa38a881..0fba890e 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionarySearchResult/DictionarySearchResultViewController.swift @@ -67,9 +67,9 @@ public extension DictionarySearchResultViewController { let type = reactor.currentState.type // 기존 viewControllers 제거 - for vc in viewControllers { - vc.removeFromParent() - vc.view.removeFromSuperview() + for viewController in viewControllers { + viewController.removeFromParent() + viewController.view.removeFromSuperview() } // 새로운 viewControllers 생성 diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift index 920a886b..b3eeb999 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift @@ -467,7 +467,7 @@ extension ItemFilterBottomSheetViewController { .skip(1) .withUnretained(self) .subscribe { owner, scrolls in - guard let dataSource = owner.dataSource else { return } + guard owner.dataSource != nil else { return } var snapshot = owner.dataSource.snapshot() snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .scrollCategories)) snapshot.appendItems(scrolls.scrollTypes.map { .scrollCategories($0) }, toSection: .scrollCategories) diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseView.swift b/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseView.swift index b55f2db9..5ff7b1a1 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseView.swift +++ b/MLS/Presentation/MyPageFeature/MyPageFeature/CustomerSupport/CustomerSupportBaseView.swift @@ -29,7 +29,21 @@ final class CustomerSupportBaseView: UIView { public let backButton: UIButton = { let button = UIButton() - button.setImage(DesignSystemAsset.image(named: "arrowBack")?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: Constant.iconInset, left: Constant.iconInset, bottom: Constant.iconInset, right: Constant.iconInset)), for: .normal) + button + .setImage( + DesignSystemAsset + .image(named: "arrowBack")? + .withRenderingMode(.alwaysTemplate) + .resizableImage( + withCapInsets: UIEdgeInsets( + top: Constant.iconInset, + left: Constant.iconInset, + bottom: Constant.iconInset, + right: Constant.iconInset + ) + ), + for: .normal + ) button.tintColor = .textColor return button }() diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainCell.swift b/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainCell.swift index e17e19f3..7b8915cc 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainCell.swift +++ b/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainCell.swift @@ -24,11 +24,11 @@ public final class MyPageMainCell: UICollectionViewCell { // MARK: - Components private let imageView: UIImageView = { - let iv = UIImageView() - iv.contentMode = .scaleAspectFill - iv.clipsToBounds = true - iv.layer.cornerRadius = Constant.imageSize / 2 - return iv + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFill + imageView.clipsToBounds = true + imageView.layer.cornerRadius = Constant.imageSize / 2 + return imageView }() private let nameLabel: UILabel = { diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainFactoryImpl.swift b/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainFactoryImpl.swift index 52bc16c8..6c45c1f2 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainFactoryImpl.swift +++ b/MLS/Presentation/MyPageFeature/MyPageFeature/Main/MyPageMainFactoryImpl.swift @@ -11,7 +11,14 @@ public final class MyPageMainFactoryImpl: MyPageMainFactory { private let setCharacterFactory: SetCharacterFactory private let fetchProfileUseCase: FetchProfileUseCase - public init(loginFactory: LoginFactory, setProfileFactory: SetProfileFactory, customerSupportFactory: CustomerSupportFactory, notificationSettingFactory: NotificationSettingFactory, setCharacterFactory: SetCharacterFactory, fetchProfileUseCase: FetchProfileUseCase) { + public init( + loginFactory: LoginFactory, + setProfileFactory: SetProfileFactory, + customerSupportFactory: CustomerSupportFactory, + notificationSettingFactory: NotificationSettingFactory, + setCharacterFactory: SetCharacterFactory, + fetchProfileUseCase: FetchProfileUseCase + ) { self.loginFactory = loginFactory self.setProfileFactory = setProfileFactory self.customerSupportFactory = customerSupportFactory @@ -21,7 +28,13 @@ public final class MyPageMainFactoryImpl: MyPageMainFactory { } public func make() -> BaseViewController { - let viewController = MyPageMainViewController(setProfileFactory: setProfileFactory, customerSupportFactory: customerSupportFactory, notificationSettingFactory: notificationSettingFactory, setCharacterFactory: setCharacterFactory, loginFactory: loginFactory) + let viewController = MyPageMainViewController( + setProfileFactory: setProfileFactory, + customerSupportFactory: customerSupportFactory, + notificationSettingFactory: notificationSettingFactory, + setCharacterFactory: setCharacterFactory, + loginFactory: loginFactory + ) viewController.reactor = MyPageMainReactor(fetchProfileUseCase: fetchProfileUseCase) return viewController } diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingView.swift b/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingView.swift index a992b752..b4e3c348 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingView.swift +++ b/MLS/Presentation/MyPageFeature/MyPageFeature/NotificationSetting/NotificationSettingView.swift @@ -22,7 +22,21 @@ final class NotificationSettingView: UIView { public let headerView = UIView() public let backButton: UIButton = { let button = UIButton() - button.setImage(DesignSystemAsset.image(named: "arrowBack")?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: Constant.iconInset, left: Constant.iconInset, bottom: Constant.iconInset, right: Constant.iconInset)), for: .normal) + button + .setImage( + DesignSystemAsset + .image(named: "arrowBack")? + .withRenderingMode(.alwaysTemplate) + .resizableImage( + withCapInsets: UIEdgeInsets( + top: Constant.iconInset, + left: Constant.iconInset, + bottom: Constant.iconInset, + right: Constant.iconInset + ) + ), + for: .normal + ) button.tintColor = .textColor return button }() diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileFactoryImpl.swift b/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileFactoryImpl.swift index dfaac96f..59352520 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileFactoryImpl.swift +++ b/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileFactoryImpl.swift @@ -10,7 +10,14 @@ public final class SetProfileFactoryImpl: SetProfileFactory { private let withdrawUseCase: WithdrawUseCase private let fetchProfileUseCase: FetchProfileUseCase - public init(selectImageFactory: SelectImageFactory, checkNickNameUseCase: CheckNickNameUseCase, updateNickNameUseCase: UpdateNickNameUseCase, logoutUseCase: LogoutUseCase, withdrawUseCase: WithdrawUseCase, fetchProfileUseCase: FetchProfileUseCase) { + public init( + selectImageFactory: SelectImageFactory, + checkNickNameUseCase: CheckNickNameUseCase, + updateNickNameUseCase: UpdateNickNameUseCase, + logoutUseCase: LogoutUseCase, + withdrawUseCase: WithdrawUseCase, + fetchProfileUseCase: FetchProfileUseCase + ) { self.selectImageFactory = selectImageFactory self.checkNickNameUseCase = checkNickNameUseCase self.updateNickNameUseCase = updateNickNameUseCase diff --git a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/AppDelegate.swift b/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/AppDelegate.swift index 2147726d..a643b7e0 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/AppDelegate.swift +++ b/MLS/Presentation/MyPageFeature/MyPageFeatureDemo/AppDelegate.swift @@ -1,4 +1,3 @@ -// swiftlint:disable function_body_length // swiftlint:disable line_length import UIKit From c5e32ec8bc1ff45b9e78f852c5745d59511072e7 Mon Sep 17 00:00:00 2001 From: p2glet Date: Wed, 26 Nov 2025 18:03:27 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat/#269:=20=EC=BB=AC=EB=A0=89=EC=85=98?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?(=EB=B6=81=EB=A7=88=ED=81=AC=20/=20=EC=9D=B4=EB=A6=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Endpoints/CollectionEndPoint.swift | 13 ++++- .../CollectionAPIRepositoryImpl.swift | 24 ++++++++- .../AddCollectionsToBookmarkUseCaseImpl.swift | 1 - .../Collection/SetCollectionUseCaseImpl.swift | 15 ++++++ .../Error/DomainHTTPError.swift.swift | 9 ++++ .../Repository/CollectionAPIRepository.swift | 2 + .../Collection/SetCollectionUseCase.swift | 5 ++ MLS/MLS/Application/AppDelegate.swift | 9 ++-- .../AddCollectionFactoryImpl.swift | 13 +++-- .../AddCollection/AddCollectionReactor.swift | 35 +++++++------ .../AddCollectionViewController.swift | 38 ++++++++------ .../BookmarkListViewController.swift | 2 +- .../BookmarkModalFactoryImpl.swift | 12 +++-- .../BookmarkModal/BookmarkModalReactor.swift | 22 ++++++-- .../BookmarkModalViewController.swift | 23 ++++++--- .../CollectionDetailView.swift | 4 ++ .../CollectionDetailViewController.swift | 33 +++++++++--- .../CollectionEditFactoryImpl.swift | 4 +- .../CollectionEditReactor.swift | 8 +-- .../CollectionEditViewController.swift | 44 ++++++++-------- .../CollectionListFactoryImpl.swift | 10 ++-- .../CollectionListReactor.swift | 21 +++----- .../CollectionListViewController.swift | 19 ++++--- .../AddCollectionFactory.swift | 2 +- .../BookmarkModalFactory.swift | 3 +- .../CollectionEditFactory.swift | 3 +- .../Components/NavigationBar.swift | 6 +++ .../DictionaryDetailBaseViewController.swift | 20 ++++---- .../DictionaryListViewController.swift | 51 +++++++------------ 29 files changed, 287 insertions(+), 164 deletions(-) create mode 100644 MLS/Domain/Domain/UseCaseImpl/Collection/SetCollectionUseCaseImpl.swift create mode 100644 MLS/Domain/DomainInterface/Error/DomainHTTPError.swift.swift create mode 100644 MLS/Domain/DomainInterface/UseCase/Collection/SetCollectionUseCase.swift diff --git a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift index 84ad17fc..f24e6688 100644 --- a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift @@ -19,7 +19,7 @@ public enum CollectionEndPoint { ) } - public static func addBookmarksToCollection(id: Int, body: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { + public static func addBookmarksToCollection(id: Int, body: Encodable) -> EndPoint { .init( baseURL: base, path: "/api/v1/collections/\(id)/bookmarks", @@ -28,7 +28,7 @@ public enum CollectionEndPoint { ) } - public static func addCollectionsToBookmark(id: Int, body: Encodable) -> ResponsableEndPoint<[BookmarkDTO]> { + public static func addCollectionsToBookmark(id: Int, body: Encodable) -> EndPoint { .init( baseURL: base, path: "/api/v1/bookmarks/\(id)/collections", @@ -36,4 +36,13 @@ public enum CollectionEndPoint { body: body ) } + + public static func setCollectionName(id: Int, body: Encodable) -> EndPoint { + .init( + baseURL: base, + path: "/api/v1/collections/\(id)", + method: .PUT, + body: body + ) + } } diff --git a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift index 2a105e2f..315f6ed1 100644 --- a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift +++ b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift @@ -34,13 +34,29 @@ public class CollectionAPIRepositoryImpl: CollectionAPIRepository { public func addBookmarksToCollection(collectionId: Int, bookmarkIds: [Int]) -> Completable { let endPoint = CollectionEndPoint.addBookmarksToCollection(id: collectionId, body: AddBookmarkRequestBody(bookmarkIds: bookmarkIds)) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) + .catch { error in + if let netErr = error as? NetworkError { + switch netErr { + case let .statusError(code, body): + return .error(DomainHTTPError.httpStatus(code: code, message: body)) + default: + return .error(DomainHTTPError.unknown) + } + } else { + return .error(DomainHTTPError.unknown) + } + } } - + public func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable { let endPoint = CollectionEndPoint.addCollectionsToBookmark(id: bookmarkId, body: AddCollectionRequestBody(collectionIds: collectionIds)) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) } - + + public func setCollectionName(collectionId: Int, name: String) -> Completable { + let endPoint = CollectionEndPoint.setCollectionName(id: collectionId, body: SetCollectionRequestBody(name: name)) + return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) + } } private extension CollectionAPIRepositoryImpl { @@ -55,4 +71,8 @@ private extension CollectionAPIRepositoryImpl { struct AddCollectionRequestBody: Encodable { let collectionIds: [Int] } + + struct SetCollectionRequestBody: Encodable { + let name: String + } } diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift index 02f97bb5..456763f9 100644 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift @@ -1,5 +1,4 @@ import DomainInterface - import RxSwift public final class AddCollectionsToBookmarkUseCaseImpl: AddCollectionsToBookmarkUseCase { diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/SetCollectionUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/SetCollectionUseCaseImpl.swift new file mode 100644 index 00000000..7e9912ac --- /dev/null +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/SetCollectionUseCaseImpl.swift @@ -0,0 +1,15 @@ +import DomainInterface + +import RxSwift + +public final class SetCollectionUseCaseImpl: SetCollectionUseCase { + private let repository: CollectionAPIRepository + + public init(repository: CollectionAPIRepository) { + self.repository = repository + } + + public func execute(collectionId: Int, name: String) -> Completable { + return repository.setCollectionName(collectionId: collectionId, name: name) + } +} diff --git a/MLS/Domain/DomainInterface/Error/DomainHTTPError.swift.swift b/MLS/Domain/DomainInterface/Error/DomainHTTPError.swift.swift new file mode 100644 index 00000000..209c66ad --- /dev/null +++ b/MLS/Domain/DomainInterface/Error/DomainHTTPError.swift.swift @@ -0,0 +1,9 @@ +import Foundation + +public enum DomainHTTPError: Error, Equatable { + case httpStatus(code: Int, message: String?) + case network + case decode + case noData + case unknown +} diff --git a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift index 16029db8..a7ec5437 100644 --- a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift +++ b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift @@ -13,4 +13,6 @@ public protocol CollectionAPIRepository { func addBookmarksToCollection(collectionId: Int, bookmarkIds: [Int]) -> Completable func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable + + func setCollectionName(collectionId: Int, name: String) -> Completable } diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/SetCollectionUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/SetCollectionUseCase.swift new file mode 100644 index 00000000..39cea79d --- /dev/null +++ b/MLS/Domain/DomainInterface/UseCase/Collection/SetCollectionUseCase.swift @@ -0,0 +1,5 @@ +import RxSwift + +public protocol SetCollectionUseCase { + func execute(collectionId: Int, name: String) -> Completable +} diff --git a/MLS/MLS/Application/AppDelegate.swift b/MLS/MLS/Application/AppDelegate.swift index c84a13ce..58f66f44 100644 --- a/MLS/MLS/Application/AppDelegate.swift +++ b/MLS/MLS/Application/AppDelegate.swift @@ -513,6 +513,9 @@ extension AppDelegate { DIContainer.register(type: AddCollectionsToBookmarkUseCase.self) { AddCollectionsToBookmarkUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) } + DIContainer.register(type: SetCollectionUseCase.self) { + SetCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + } } fileprivate func registerFactory() { @@ -526,7 +529,7 @@ extension AppDelegate { SortedBottomSheetFactoryImpl() } DIContainer.register(type: AddCollectionFactory.self) { - AddCollectionFactoryImpl() + AddCollectionFactoryImpl(createCollectionListUseCase: DIContainer.resolve(type: CreateCollectionListUseCase.self), setCollectionUseCase: DIContainer.resolve(type: SetCollectionUseCase.self)) } DIContainer.register(type: BookmarkModalFactory.self) { BookmarkModalFactoryImpl( @@ -793,10 +796,8 @@ extension AppDelegate { } DIContainer.register(type: CollectionListFactory.self) { CollectionListFactoryImpl( - collectionListUseCase: DIContainer.resolve( + fetchCollectionListUseCase: DIContainer.resolve( type: FetchCollectionListUseCase.self), - createCollectionListUseCase: DIContainer.resolve( - type: CreateCollectionListUseCase.self), addCollectionFactory: DIContainer.resolve( type: AddCollectionFactory.self), bookmarkDetailFactory: DIContainer.resolve( diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift index 1480acd1..e15a72f7 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift @@ -3,12 +3,17 @@ import BookmarkFeatureInterface import DomainInterface public final class AddCollectionFactoryImpl: AddCollectionFactory { - public init() {} + private let createCollectionListUseCase: CreateCollectionListUseCase + private let setCollectionUseCase: SetCollectionUseCase + + public init(createCollectionListUseCase: CreateCollectionListUseCase, setCollectionUseCase: SetCollectionUseCase) { + self.createCollectionListUseCase = createCollectionListUseCase + self.setCollectionUseCase = setCollectionUseCase + } - public func make(collection: CollectionResponse?, onDismissWithMessage: @escaping (CollectionResponse?) -> Void) -> BaseViewController { + public func make(collection: CollectionResponse?) -> BaseViewController { let viewController = AddCollectionViewController() - viewController.reactor = AddCollectionModalReactor(collection: collection) -// viewController.onDismissWithMessage = onDismissWithMessage + viewController.reactor = AddCollectionModalReactor(collection: collection, createCollectionListUseCase: createCollectionListUseCase, setCollectionUseCase: setCollectionUseCase) return viewController } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionReactor.swift index d4c74a4e..4f2cd581 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionReactor.swift @@ -7,7 +7,7 @@ public final class AddCollectionModalReactor: Reactor { // MARK: - Route public enum Route { case dismiss - case dismissWithSuccess(CollectionResponse?) + case dismissWithData } // MARK: - Action @@ -21,7 +21,6 @@ public final class AddCollectionModalReactor: Reactor { public enum Mutation { case saveInput(String) case setError(Bool) - case addCollection(String) case setButtonEnabled(Bool) case toNavigate(Route) } @@ -38,10 +37,14 @@ public final class AddCollectionModalReactor: Reactor { // MARK: - Properties public var initialState = State(collection: nil) + private let createCollectionListUseCase: CreateCollectionListUseCase + private let setCollectionUseCase: SetCollectionUseCase + // MARK: - Init - public init(collection: CollectionResponse?) { - self.initialState.collection = collection - self.initialState.inputText = collection?.name + public init(collection: CollectionResponse?, createCollectionListUseCase: CreateCollectionListUseCase, setCollectionUseCase: SetCollectionUseCase) { + self.initialState = State(collection: collection, inputText: collection?.name) + self.createCollectionListUseCase = createCollectionListUseCase + self.setCollectionUseCase = setCollectionUseCase } // MARK: - Mutate @@ -60,10 +63,21 @@ public final class AddCollectionModalReactor: Reactor { case .completeButtonTapped: guard let text = currentState.inputText else { return .empty() } let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines) + if trimmed.count > 18 { return .just(.setError(true)) + } + + if currentState.collection == nil { + return createCollectionListUseCase.execute(name: trimmed) + .andThen(.just(.toNavigate(.dismissWithData))) } else { - return .just(.addCollection(trimmed)) + guard let id = currentState.collection?.collectionId else { return .empty() } + return setCollectionUseCase.execute( + collectionId: id, + name: trimmed + ) + .andThen(.just(.toNavigate(.dismissWithData))) } } } @@ -81,15 +95,6 @@ public final class AddCollectionModalReactor: Reactor { newState.isButtonEnabled = isEnabled case .toNavigate(let route): newState.route = route - case .addCollection(let name): - var collection = newState.collection - // 기존 collection이 없으면 새로 생성 - if collection == nil { - collection = CollectionResponse(collectionId: -1, name: "", createdAt: [], recentBookmarks: []) - } else { - collection?.name = name - } - newState.route = .dismissWithSuccess(collection) } return newState diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift index 14ae3f7e..ce534226 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift @@ -15,7 +15,7 @@ public final class AddCollectionViewController: BaseViewController, View { // MARK: - Properties public var disposeBag = DisposeBag() -// public var onDismissWithMessage: ((BookmarkCollection?) -> Void)? + public let dismissed = PublishSubject() // MARK: - Components private let mainView = AddCollectionView() @@ -94,7 +94,7 @@ private extension AddCollectionViewController { tapGesture.rx.event .withUnretained(self) .subscribe(onNext: { owner, _ in - owner.dismissWithAnimation() + owner.dismissWithAnimation(withData: false) }) .disposed(by: disposeBag) } @@ -102,7 +102,6 @@ private extension AddCollectionViewController { func setupKeyboard() { setupKeyboard(inset: AddCollectionView.Constant.buttonBottomMargin) { [weak self] height in self?.mainView.addButtonBottomConstraint?.update(inset: height) - self?.mainView.updateTextField(text: self?.reactor?.initialState.inputText) } } } @@ -116,6 +115,7 @@ extension AddCollectionViewController { private func bindUserActions(reactor: Reactor) { mainView.inputTextField.rx.text + .distinctUntilChanged() .map { Reactor.Action.inputTextChanged($0) } .bind(to: reactor.action) .disposed(by: disposeBag) @@ -132,6 +132,15 @@ extension AddCollectionViewController { } private func bindState(reactor: Reactor) { + reactor.state + .map(\.inputText) + .take(1) + .withUnretained(self) + .bind(onNext: { owner, text in + owner.mainView.updateTextField(text: text) + }) + .disposed(by: disposeBag) + reactor.state .map(\.isError) .distinctUntilChanged() @@ -167,14 +176,12 @@ extension AddCollectionViewController { .subscribe(onNext: { owner, route in switch route { case .dismiss: - owner.dismissWithAnimation { + owner.dismissWithAnimation(withData: false) { owner.dismiss(animated: false) } - case .dismissWithSuccess(let collectionName): - owner.dismissWithAnimation { - owner.dismiss(animated: false) { -// owner.onDismissWithMessage?(collectionName) - } + case .dismissWithData: + owner.dismissWithAnimation(withData: true) { + owner.dismiss(animated: false) } default: break @@ -197,13 +204,12 @@ private extension AddCollectionViewController { self.addCollectionContainer.transform = .identity } - mainView.inputTextField.text = nil mainView.setError(isError: false) mainView.setButtonEnabled(isEnabled: false) mainView.inputTextField.becomeFirstResponder() } - func dismissWithAnimation(shouldDismissVC: Bool = true, completion: (() -> Void)? = nil) { + func dismissWithAnimation(withData: Bool, completion: (() -> Void)? = nil) { mainView.endEditing(true) UIView.animate(withDuration: 0.25, animations: { @@ -213,11 +219,13 @@ private extension AddCollectionViewController { self.addCollectionContainer.isHidden = true self.dimmedBackgroundView.isHidden = true - if shouldDismissVC { - self.dismiss(animated: false, completion: completion) - } else { - completion?() + if withData { + guard let text = self.mainView.inputTextField.text else { return } + self.dismissed.onNext((text)) + self.dismissed.onCompleted() } + + self.dismiss(animated: false, completion: completion) }) } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift index 52de21b4..2bec443e 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift @@ -181,7 +181,7 @@ extension BookmarkListViewController { tabBarController.selectTab(index: 0) } case .edit: - let viewController = owner.collectionEditFactory.make() + let viewController = owner.collectionEditFactory.make(bookmarks: reactor.currentState.items) owner.navigationController?.pushViewController(viewController, animated: true) default: break diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift index 3ba23963..878fe766 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift @@ -13,12 +13,18 @@ public final class BookmarkModalFactoryImpl: BookmarkModalFactory { self.addCollectionsToBookmarkUseCase = addCollectionsToBookmarkUseCase } - public func make(bookmarkId: Int, onDismissWithColletions: (([CollectionResponse?]) -> Void)?, onDismissWithMessage: ((CollectionResponse?) -> Void)?) -> BaseViewController { + public func make(bookmarkId: Int) -> BaseViewController { let reactor = BookmarkModalReactor(bookmarkId: bookmarkId, fetchCollectionListUseCase: fetchCollectionListUseCase, addCollectionsToBookmarkUseCase: addCollectionsToBookmarkUseCase) let viewController = BookmarkModalViewController(addCollectionFactory: addCollectionFactory) viewController.reactor = reactor - viewController.onDismissWithMessage = onDismissWithMessage - viewController.onDismissWithCollections = onDismissWithColletions + return viewController + } + + public func make(bookmarkId: Int, onComplete: ((Bool) -> Void)? = nil) -> BaseViewController { + let viewController = make(bookmarkId: bookmarkId) + if let viewController = viewController as? BookmarkModalViewController { + viewController.onCompleted = onComplete + } return viewController } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift index 3e1f84c7..38a71be3 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift @@ -2,17 +2,20 @@ import BookmarkFeatureInterface import DomainInterface import ReactorKit +import RxSwift public final class BookmarkModalReactor: Reactor { public enum Route { case none case dismiss + case dismissWithData case addCollection } public enum Action { case backButtonTapped case addButtonTapped + case completeAdding case addCollectionTapped case selectItem(Int) case viewWillAppear @@ -46,16 +49,29 @@ public final class BookmarkModalReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { - case .viewWillAppear: + case .viewWillAppear, .completeAdding: return fetchCollectionListUseCase.execute() .map { .setCollection($0) } + case .addButtonTapped: - return addCollectionsToBookmarkUseCase.execute(bookmarkId: currentState.bookmarkId, collectionIds: currentState.selectedItems.map { $0.collectionId }) - .andThen(.just(.toNavigate(.dismiss))) + return addCollectionsToBookmarkUseCase + .execute( + bookmarkId: currentState.bookmarkId, + collectionIds: currentState.selectedItems.map { $0.collectionId } + ) + .do(onError: { error in + if let error = error as? DomainHTTPError { + print(error) + } + }) + .andThen(.just(.toNavigate(.dismissWithData))) + case .backButtonTapped: return .just(.toNavigate(.dismiss)) + case .addCollectionTapped: return .just(.toNavigate(.addCollection)) + case .selectItem(let id): var newItems = currentState.selectedItems if let index = newItems.firstIndex(where: { $0.collectionId == id }) { diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift index 4c3c38f4..2711e246 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalViewController.swift @@ -14,9 +14,7 @@ public final class BookmarkModalViewController: BaseViewController, View { // MARK: - Properties public var disposeBag = DisposeBag() - - public var onDismissWithMessage: ((CollectionResponse?) -> Void)? - public var onDismissWithCollections: (([CollectionResponse?]) -> Void)? + public var onCompleted: ((Bool) -> Void)? private let addCollectionFactory: AddCollectionFactory @@ -116,18 +114,29 @@ extension BookmarkModalViewController { .disposed(by: disposeBag) rx.viewDidAppear - .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } .withUnretained(self) .observe(on: MainScheduler.instance) .subscribe(onNext: { owner, route in switch route { + case .dismissWithData: + owner.onCompleted?(true) + owner.dismiss(animated: true) case .dismiss: + owner.onCompleted?(false) owner.dismiss(animated: true) case .addCollection: - let viewController = owner.addCollectionFactory.make( collection: nil, onDismissWithMessage: { [weak self] collection in - self?.onDismissWithMessage?(collection) - }) + let viewController = owner.addCollectionFactory.make(collection: nil) + + guard let viewController = viewController as? AddCollectionViewController else { return } + + viewController.dismissed + .withUnretained(self) + .subscribe { owner, _ in + owner.reactor?.action.onNext(.completeAdding) + } + .disposed(by: owner.disposeBag) + owner.present(viewController, animated: true) default: break diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift index aa676d30..bec7fabe 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift @@ -94,4 +94,8 @@ extension CollectionDetailView { listCollectionView.isHidden = isEmpty emptyContainerView.isHidden = !isEmpty } + + func setName(name: String) { + navigation.setTitle(title: name) + } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift index 1152b822..f6256e61 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift @@ -128,10 +128,21 @@ extension CollectionDetailViewController { reactor.state .map(\.collection.recentBookmarks) .distinctUntilChanged() + .withUnretained(self) + .observe(on: MainScheduler.instance) + .bind(onNext: { owner, items in + owner.mainView.listCollectionView.reloadData() + owner.mainView.isEmptyData(isEmpty: items.isEmpty) + }) + .disposed(by: disposeBag) + + reactor.state + .map(\.collection.name) + .distinctUntilChanged() + .withUnretained(self) .observe(on: MainScheduler.instance) - .bind(onNext: { [weak self] items in - self?.mainView.listCollectionView.reloadData() - self?.mainView.isEmptyData(isEmpty: items.isEmpty) + .bind(onNext: { owner, name in + owner.mainView.setName(name: name) }) .disposed(by: disposeBag) @@ -141,13 +152,19 @@ extension CollectionDetailViewController { .bind(onNext: { owner, menu in switch menu { case .editBookmark: - let viewController = owner.collectionEditFactory.make() + let viewController = owner.collectionEditFactory.make(bookmarks: reactor.currentState.collection.recentBookmarks) owner.navigationController?.pushViewController(viewController, animated: true) case .editName: - let viewController = owner.addCollectionFactory.make(collection: reactor.currentState.collection, onDismissWithMessage: { collection in - guard let collection = collection else { return } - reactor.action.onNext(.changeName(collection.name)) - }) + let viewController = owner.addCollectionFactory.make(collection: reactor.currentState.collection) + + guard let viewController = viewController as? AddCollectionViewController else { return } + + viewController.dismissed + .subscribe { name in + reactor.action.onNext(.changeName(name)) + } + .disposed(by: owner.disposeBag) + owner.present(viewController, animated: true) case .delete: GuideAlertFactory.show( diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditFactoryImpl.swift index 020c1c91..02533220 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditFactoryImpl.swift @@ -11,8 +11,8 @@ public final class CollectionEditFactoryImpl: CollectionEditFactory { self.bookmarkModalFactory = bookmarkModalFactory } - public func make() -> BaseViewController { - let reactor = CollectionEditReactor() + public func make(bookmarks: [BookmarkResponse]) -> BaseViewController { + let reactor = CollectionEditReactor(bookmarks: bookmarks) let viewController = CollectionEditViewController(bookmarkModalFactory: bookmarkModalFactory) viewController.reactor = reactor viewController.isBottomTabbarHidden = true diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditReactor.swift index 383850ca..924a8665 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditReactor.swift @@ -28,7 +28,7 @@ public final class CollectionEditReactor: Reactor { public struct State { @Pulse var route: Route - var collection: CollectionResponse + var bookmarks: [BookmarkResponse] var selectedItems = [BookmarkResponse]() var selectedCollections = [CollectionResponse]() } @@ -38,8 +38,8 @@ public final class CollectionEditReactor: Reactor { private let disposeBag = DisposeBag() - public init() { - self.initialState = State(route: .none, collection: CollectionResponse(collectionId: -1, name: "", createdAt: [], recentBookmarks: [])) + public init(bookmarks: [BookmarkResponse]) { + self.initialState = State(route: .none, bookmarks: bookmarks) } public func mutate(action: Action) -> Observable { @@ -56,7 +56,7 @@ public final class CollectionEditReactor: Reactor { // addCollection에서 선택된 컬렉션 목록 저장 return .empty() case .itemTapped(let index): - let item = currentState.collection.recentBookmarks[index] + let item = currentState.bookmarks[index] var newItems = currentState.selectedItems if let index = newItems.firstIndex(where: { $0.originalId == item.originalId }) { newItems.remove(at: index) diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift index 471e06a2..f85d8586 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift @@ -98,9 +98,10 @@ extension CollectionEditViewController { .disposed(by: disposeBag) reactor.state - .map(\.collection.recentBookmarks) + .map(\.bookmarks) .distinctUntilChanged() .withUnretained(self) + .observe(on: MainScheduler.instance) .subscribe { owner, _ in owner.mainView.listCollectionView.reloadData() } @@ -110,6 +111,7 @@ extension CollectionEditViewController { .map(\.selectedItems) .distinctUntilChanged() .withUnretained(self) + .observe(on: MainScheduler.instance) .subscribe { owner, _ in owner.mainView.listCollectionView.reloadData() } @@ -119,17 +121,14 @@ extension CollectionEditViewController { .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } .withUnretained(self) + .observe(on: MainScheduler.instance) .subscribe { owner, route in switch route { case .dismiss: owner.navigationController?.popViewController(animated: true) case .collcectionList: // 수정필요 bookmarkId -> bookmarkIds - let viewController = owner.bookmarkModalFactory.make(bookmarkId: reactor.currentState.selectedCollections.first?.collectionId ?? 0, onDismissWithColletions: { _ in - - }, onDismissWithMessage: { _ in - - }) + let viewController = owner.bookmarkModalFactory.make(bookmarkId: reactor.currentState.selectedItems.first?.bookmarkId ?? 0) owner.present(viewController, animated: true) default: break @@ -142,7 +141,7 @@ extension CollectionEditViewController { // MARK: - Delegate extension CollectionEditViewController: UICollectionViewDelegate, UICollectionViewDataSource { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - reactor?.currentState.collection.recentBookmarks.count ?? 0 + reactor?.currentState.bookmarks.count ?? 0 } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { @@ -151,25 +150,28 @@ extension CollectionEditViewController: UICollectionViewDelegate, UICollectionVi withReuseIdentifier: DictionaryListCell.identifier, for: indexPath ) as? DictionaryListCell, - let item = reactor?.currentState.collection.recentBookmarks[indexPath.row] + let item = reactor?.currentState.bookmarks[indexPath.row] else { return UICollectionViewCell() } let isSelected = reactor?.currentState.selectedItems.contains(where: { $0.originalId == item.originalId }) ?? false + var subText: String? { + [.item, .monster, .quest].contains(item.type) ? item.level.map { "Lv. \($0)" } : nil + } -// cell.inject( -// type: .checkbox, -// input: DictionaryListCell.Input( -// type: item.type, -// mainText: item.mainText, -// subText: item.subText, -// image: item.image, -// isSelected: isSelected -// ), -// onIconTapped: { [weak self] _ in -// self?.reactor?.action.onNext(.itemTapped(indexPath.row)) -// } -// ) + cell.inject( + type: .checkbox, + input: DictionaryListCell.Input( + type: item.type, + mainText: item.name, + subText: subText, + imageUrl: item.imageUrl ?? "", + isBookmarked: isSelected + ), + onBookmarkTapped: { [weak self] _ in + self?.reactor?.action.onNext(.itemTapped(indexPath.row)) + } + ) return cell } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift index 8fd68d44..074ff62a 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift @@ -3,20 +3,18 @@ import BookmarkFeatureInterface import DomainInterface public final class CollectionListFactoryImpl: CollectionListFactory { - private let collectionListUseCase: FetchCollectionListUseCase - private let createCollectionListUseCase: CreateCollectionListUseCase + private let fetchCollectionListUseCase: FetchCollectionListUseCase private let addCollectionFactory: AddCollectionFactory private let bookmarkDetailFactory: CollectionDetailFactory - public init(collectionListUseCase: FetchCollectionListUseCase, createCollectionListUseCase: CreateCollectionListUseCase, addCollectionFactory: AddCollectionFactory, bookmarkDetailFactory: CollectionDetailFactory) { - self.collectionListUseCase = collectionListUseCase - self.createCollectionListUseCase = createCollectionListUseCase + public init(fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionFactory: AddCollectionFactory, bookmarkDetailFactory: CollectionDetailFactory) { + self.fetchCollectionListUseCase = fetchCollectionListUseCase self.addCollectionFactory = addCollectionFactory self.bookmarkDetailFactory = bookmarkDetailFactory } public func make() -> BaseViewController { - let reactor = CollectionListReactor(collectionListUseCase: collectionListUseCase, createCollectionListUseCase: createCollectionListUseCase) + let reactor = CollectionListReactor(fetchCollectionListUseCase: fetchCollectionListUseCase) let viewController = CollectionListViewController(addCollectionFactory: addCollectionFactory, detailFactory: bookmarkDetailFactory) viewController.reactor = reactor return viewController diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift index f0443c02..f196bee0 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift @@ -14,7 +14,7 @@ public final class CollectionListReactor: Reactor { public enum Action { case itemTapped(Int) case viewWillAppear - case addCollection(String) + case completeAdding } public enum Mutation { @@ -29,29 +29,24 @@ public final class CollectionListReactor: Reactor { // MARK: - Properties public var initialState: State - private let disposeBag = DisposeBag() - private let collectionListUseCase: FetchCollectionListUseCase - private let createCollectionListUseCase: CreateCollectionListUseCase + private let fetchCollectionListUseCase: FetchCollectionListUseCase + - public init( - collectionListUseCase: FetchCollectionListUseCase, - createCollectionListUseCase: CreateCollectionListUseCase - ) { - self.collectionListUseCase = collectionListUseCase - self.createCollectionListUseCase = createCollectionListUseCase + public init(fetchCollectionListUseCase: FetchCollectionListUseCase) { + self.fetchCollectionListUseCase = fetchCollectionListUseCase self.initialState = State(route: .none, collectionList: []) } public func mutate(action: Action) -> Observable { switch action { case .viewWillAppear: - return collectionListUseCase.execute().map { .setListData($0) } + return fetchCollectionListUseCase.execute().map { .setListData($0) } case .itemTapped(let index): return .just(.navigateTo(.detail(currentState.collectionList[index]))) - case .addCollection(let collection): - return createCollectionListUseCase.execute(name: collection).andThen(collectionListUseCase.execute()) + case .completeAdding: + return fetchCollectionListUseCase.execute() .map {.setListData($0)} } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift index 56926363..6b0d8fd1 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift @@ -62,14 +62,16 @@ private extension CollectionListViewController { addFloatingButton { [weak self] in guard let self = self else { return } -// let viewController = self.addCollectionFactory.make(collection: nil, onDismissWithMessage: { [weak self] collection in -// if let collection = collection { -// // Reactor에게 새로운 콜렉션 추가를 알림 -// self?.reactor?.action.onNext(.addCollection(collection.title)) -// } -// self?.onDismissWithMessage?(collection) -// }) - let viewController = self.addCollectionFactory.make(collection: nil, onDismissWithMessage: { _ in }) + let viewController = self.addCollectionFactory.make(collection: nil) + + guard let viewController = viewController as? AddCollectionViewController else { return } + viewController.dismissed + .withUnretained(self) + .subscribe { owner, _ in + owner.reactor?.action.onNext(.completeAdding) + } + .disposed(by: disposeBag) + self.present(viewController, animated: true) } } @@ -102,6 +104,7 @@ extension CollectionListViewController { .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } .withUnretained(self) + .observe(on: MainScheduler.instance) .subscribe { owner, route in switch route { case .detail(let collection): diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/AddCollectionFactory.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/AddCollectionFactory.swift index 21449643..de84a042 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/AddCollectionFactory.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/AddCollectionFactory.swift @@ -2,5 +2,5 @@ import BaseFeature import DomainInterface public protocol AddCollectionFactory { - func make(collection: CollectionResponse?, onDismissWithMessage: @escaping (CollectionResponse?) -> Void) -> BaseViewController + func make(collection: CollectionResponse?) -> BaseViewController } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift index 46866daf..efee86e8 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift @@ -2,5 +2,6 @@ import BaseFeature import DomainInterface public protocol BookmarkModalFactory { - func make(bookmarkId: Int, onDismissWithColletions: (([CollectionResponse?]) -> Void)?, onDismissWithMessage: ((CollectionResponse?) -> Void)?) -> BaseViewController + func make(bookmarkId: Int) -> BaseViewController + func make(bookmarkId: Int, onComplete: ((Bool) -> Void)?) -> BaseViewController } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionEditFactory.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionEditFactory.swift index 2b3376ad..affdca0b 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionEditFactory.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/CollectionEditFactory.swift @@ -1,5 +1,6 @@ import BaseFeature +import DomainInterface public protocol CollectionEditFactory { - func make() -> BaseViewController + func make(bookmarks: [BookmarkResponse]) -> BaseViewController } diff --git a/MLS/Presentation/DesignSystem/DesignSystem/Components/NavigationBar.swift b/MLS/Presentation/DesignSystem/DesignSystem/Components/NavigationBar.swift index d814f5fc..a57506b9 100644 --- a/MLS/Presentation/DesignSystem/DesignSystem/Components/NavigationBar.swift +++ b/MLS/Presentation/DesignSystem/DesignSystem/Components/NavigationBar.swift @@ -174,3 +174,9 @@ private extension NavigationBar { } } } + +public extension NavigationBar { + func setTitle(title: String) { + collectionTitleLabel.text = title + } +} diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift index 78de6569..ad4defc8 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift @@ -297,16 +297,18 @@ extension DictionaryDetailBaseViewController { buttonText: "컬렉션 추가", buttonAction: { [weak self] in guard let self, - let id = bookmarkId else { return } - let viewController = self.bookmarkModalFactory.make( - bookmarkId: id, - onDismissWithColletions: { _ in }, - onDismissWithMessage: { _ in - ToastFactory.createToast( - message: "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." - ) + let id = bookmarkId else { return } + let viewController = self.bookmarkModalFactory.make(bookmarkId: id, onComplete: { isAdd in + if isAdd { + DispatchQueue.main.async { + ToastFactory.createToast( + message: + "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." + ) + } } - ) + }) + viewController.modalPresentationStyle = .pageSheet if let sheet = viewController.sheetPresentationController { sheet.detents = [.medium(), .large()] diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift index d7e95acc..fc28b1d6 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift @@ -148,7 +148,6 @@ extension DictionaryListViewController { .subscribe { owner, route in switch route { case .sort(let type): - print("sortsort실행~~~~~~~~~~~~~~~~~~~~~~~") let viewController = owner.sortedFactory.make( sortedOptions: type.bookmarkSortedFilter, selectedIndex: owner.selectedSortIndex @@ -200,29 +199,15 @@ extension DictionaryListViewController { } // MARK: - Delegate -extension DictionaryListViewController: UICollectionViewDelegate, UICollectionViewDataSource{ - public func collectionView( - _ collectionView: UICollectionView, numberOfItemsInSection section: Int - ) -> Int { +extension DictionaryListViewController: UICollectionViewDelegate, UICollectionViewDataSource { + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { guard let state = reactor?.currentState else { return 0 } - return state.listItems.count } - public func collectionView( - _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath - ) -> UICollectionViewCell { - guard let state = reactor?.currentState else { - return UICollectionViewCell() - } - guard - let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: DictionaryListCell.identifier, - for: indexPath - ) as? DictionaryListCell - else { - return UICollectionViewCell() - } + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let state = reactor?.currentState, + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DictionaryListCell.identifier, for: indexPath) as? DictionaryListCell else { return UICollectionViewCell() } let item = state.listItems[indexPath.row] var subText: String? { @@ -282,20 +267,20 @@ extension DictionaryListViewController: UICollectionViewDelegate, UICollectionVi text: "아이템을 북마크에 추가했어요.", buttonText: "컬렉션 추가", buttonAction: { - DispatchQueue.main.async { - guard let reactor = self.reactor, + DispatchQueue.main.async { [weak self] in + guard let self = self, + let reactor = self.reactor, let id = reactor.currentState.listItems[indexPath.row].bookmarkId else { return } - let viewController = self.bookmarkModalFactory - .make( - bookmarkId: id, - onDismissWithColletions: { _ in }, - onDismissWithMessage: { _ in - ToastFactory.createToast( - message: - "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." - ) - } - ) + + let viewController = self.bookmarkModalFactory.make(bookmarkId: id, onComplete: { isAdd in + if isAdd { + ToastFactory.createToast( + message: + "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." + ) + } + }) + viewController.modalPresentationStyle = .pageSheet if let sheet = viewController.sheetPresentationController { sheet.detents = [.medium(), .large()] From 195147c90c647df3adf0e7d4680a3c7b52b16be6 Mon Sep 17 00:00:00 2001 From: p2glet Date: Wed, 26 Nov 2025 21:05:45 +0900 Subject: [PATCH 05/10] =?UTF-8?q?feat/#269:=20=EC=BB=AC=EB=A0=89=EC=85=98?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/Endpoints/CollectionEndPoint.swift | 10 +++++++++- .../CollectionAPIRepositoryImpl.swift | 11 ++++++++--- .../DeleteCollectionUseCaseImpl.swift | 15 +++++++++++++++ .../Repository/CollectionAPIRepository.swift | 2 ++ .../Collection/DeleteCollectionUseCase.swift | 5 +++++ MLS/MLS/Application/AppDelegate.swift | 12 +++++++++++- .../CollectionDetailFactoryImpl.swift | 12 ++++++++++-- .../CollectionDetailReactor.swift | 18 +++++++++++++++--- .../CollectionDetailViewController.swift | 3 ++- 9 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 MLS/Domain/Domain/UseCaseImpl/Collection/DeleteCollectionUseCaseImpl.swift create mode 100644 MLS/Domain/DomainInterface/UseCase/Collection/DeleteCollectionUseCase.swift diff --git a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift index f24e6688..ab39e596 100644 --- a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift @@ -36,7 +36,7 @@ public enum CollectionEndPoint { body: body ) } - + public static func setCollectionName(id: Int, body: Encodable) -> EndPoint { .init( baseURL: base, @@ -45,4 +45,12 @@ public enum CollectionEndPoint { body: body ) } + + public static func deleteCollection(id: Int) -> EndPoint { + .init( + baseURL: base, + path: "/api/v1/collections/\(id)", + method: .DELETE + ) + } } diff --git a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift index 315f6ed1..abc3e46b 100644 --- a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift +++ b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift @@ -47,16 +47,21 @@ public class CollectionAPIRepositoryImpl: CollectionAPIRepository { } } } - + public func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable { let endPoint = CollectionEndPoint.addCollectionsToBookmark(id: bookmarkId, body: AddCollectionRequestBody(collectionIds: collectionIds)) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) } - + public func setCollectionName(collectionId: Int, name: String) -> Completable { let endPoint = CollectionEndPoint.setCollectionName(id: collectionId, body: SetCollectionRequestBody(name: name)) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) } + + public func deleteCollection(collectionId: Int) -> Completable { + let endPoint = CollectionEndPoint.deleteCollection(id: collectionId) + return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) + } } private extension CollectionAPIRepositoryImpl { @@ -71,7 +76,7 @@ private extension CollectionAPIRepositoryImpl { struct AddCollectionRequestBody: Encodable { let collectionIds: [Int] } - + struct SetCollectionRequestBody: Encodable { let name: String } diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/DeleteCollectionUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/DeleteCollectionUseCaseImpl.swift new file mode 100644 index 00000000..40b318f3 --- /dev/null +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/DeleteCollectionUseCaseImpl.swift @@ -0,0 +1,15 @@ +import DomainInterface + +import RxSwift + +public final class DeleteCollectionUseCaseImpl: DeleteCollectionUseCase { + private let repository: CollectionAPIRepository + + public init(repository: CollectionAPIRepository) { + self.repository = repository + } + + public func execute(collectionId: Int) -> Completable { + return repository.deleteCollection(collectionId: collectionId) + } +} diff --git a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift index a7ec5437..0a041212 100644 --- a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift +++ b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift @@ -15,4 +15,6 @@ public protocol CollectionAPIRepository { func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable func setCollectionName(collectionId: Int, name: String) -> Completable + + func deleteCollection(collectionId: Int) -> Completable } diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/DeleteCollectionUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/DeleteCollectionUseCase.swift new file mode 100644 index 00000000..738ccbb9 --- /dev/null +++ b/MLS/Domain/DomainInterface/UseCase/Collection/DeleteCollectionUseCase.swift @@ -0,0 +1,5 @@ +import RxSwift + +public protocol DeleteCollectionUseCase { + func execute(collectionId: Int) -> Completable +} diff --git a/MLS/MLS/Application/AppDelegate.swift b/MLS/MLS/Application/AppDelegate.swift index 58f66f44..04c25d28 100644 --- a/MLS/MLS/Application/AppDelegate.swift +++ b/MLS/MLS/Application/AppDelegate.swift @@ -516,6 +516,12 @@ extension AppDelegate { DIContainer.register(type: SetCollectionUseCase.self) { SetCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) } + DIContainer.register(type: DeleteCollectionUseCase.self) { + DeleteCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + } + DIContainer.register(type: AddBookmarksToCollectionUseCase.self) { + AddBookmarksToCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + } } fileprivate func registerFactory() { @@ -824,7 +830,11 @@ extension AppDelegate { DIContainer .resolve(type: SetBookmarkUseCase.self), fetchCollectionUseCase: DIContainer.resolve( - type: FetchCollectionUseCase.self) + type: FetchCollectionUseCase.self), + deleteCollectionUseCase: DIContainer + .resolve(type: DeleteCollectionUseCase.self), + addBookmarksToCollectionUseCase: DIContainer + .resolve(type: AddBookmarksToCollectionUseCase.self) ) } DIContainer.register(type: CollectionSettingFactory.self) { diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift index 282ff157..a9051cc4 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift @@ -15,6 +15,8 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { private let setBookmarkUseCase: SetBookmarkUseCase private let fetchCollectionUseCase: FetchCollectionUseCase + private let deleteCollectionUseCase: DeleteCollectionUseCase + private let addBookmarksToCollectionUseCase: AddBookmarksToCollectionUseCase public init( bookmarkModalFactory: BookmarkModalFactory, @@ -23,7 +25,9 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { collectionEditFactory: CollectionEditFactory, dictionaryDetailFactory: DictionaryDetailFactory, setBookmarkUseCase: SetBookmarkUseCase, - fetchCollectionUseCase: FetchCollectionUseCase + fetchCollectionUseCase: FetchCollectionUseCase, + deleteCollectionUseCase: DeleteCollectionUseCase, + addBookmarksToCollectionUseCase: AddBookmarksToCollectionUseCase ) { self.bookmarkModalFactory = bookmarkModalFactory self.collectionSettingFactory = collectionSettingFactory @@ -32,13 +36,17 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { self.dictionaryDetailFactory = dictionaryDetailFactory self.setBookmarkUseCase = setBookmarkUseCase self.fetchCollectionUseCase = fetchCollectionUseCase + self.deleteCollectionUseCase = deleteCollectionUseCase + self.addBookmarksToCollectionUseCase = addBookmarksToCollectionUseCase } public func make(collection: CollectionResponse, onMoveToMain: (() -> Void)?) -> BaseViewController { let reactor = CollectionDetailReactor( + collection: collection, setBookmarkUseCase: setBookmarkUseCase, fetchCollectionUseCase: fetchCollectionUseCase, - collection: collection + deleteCollectionUseCase: deleteCollectionUseCase, + addBookmarksToCollectionUseCase: addBookmarksToCollectionUseCase ) let viewController = CollectionDetailViewController( diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift index a40f58b7..fd0828f3 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift @@ -23,6 +23,7 @@ public final class CollectionDetailReactor: Reactor { case changeName(String) case undoLastDeletedBookmark case dataTapped(Int) + case deleteCollection } public enum Mutation { @@ -47,15 +48,19 @@ public final class CollectionDetailReactor: Reactor { // MARK: - Properties private let setBookmarkUseCase: SetBookmarkUseCase private let fetchCollectionUseCase: FetchCollectionUseCase + private let deleteCollectionUseCase: DeleteCollectionUseCase + private let addBookmarksToCollectionUseCase: AddBookmarksToCollectionUseCase public var initialState: State private let disposeBag = DisposeBag() - public init(setBookmarkUseCase: SetBookmarkUseCase, fetchCollectionUseCase: FetchCollectionUseCase, collection: CollectionResponse) { + public init(collection: CollectionResponse, setBookmarkUseCase: SetBookmarkUseCase, fetchCollectionUseCase: FetchCollectionUseCase, deleteCollectionUseCase: DeleteCollectionUseCase, addBookmarksToCollectionUseCase: AddBookmarksToCollectionUseCase) { self.initialState = State(route: .none, collection: collection) self.setBookmarkUseCase = setBookmarkUseCase self.fetchCollectionUseCase = fetchCollectionUseCase + self.deleteCollectionUseCase = deleteCollectionUseCase + self.addBookmarksToCollectionUseCase = addBookmarksToCollectionUseCase } public func mutate(action: Action) -> Observable { @@ -91,14 +96,18 @@ public final class CollectionDetailReactor: Reactor { case .changeName(let name): return .just(.setName(name)) case .undoLastDeletedBookmark: - guard let lastDeleted = currentState.lastDeletedBookmark else { return .empty() } + guard let lastDeleted = currentState.lastDeletedBookmark, + let lastDeletedBookmarkId = currentState.lastDeletedBookmark?.bookmarkId else { return .empty() } return setBookmarkUseCase.execute( bookmarkId: lastDeleted.originalId, isBookmark: .set(lastDeleted.type) ) + // 북마크 다시 설정시 이전 collection을 전부 추적해야하고 새로 바뀐 북마크ID가 필요하여 현재는 원할하게 동작하지 않음 + .andThen(addBookmarksToCollectionUseCase.execute(collectionId: currentState.collection.collectionId, bookmarkIds: [lastDeletedBookmarkId])) .andThen( Observable.concat([ - // 불러오기 + fetchCollectionUseCase.execute(id: currentState.collection.collectionId) + .map { .setItems($0) }, .just(.setLastDeletedBookmark(nil)) ]) ) @@ -106,6 +115,9 @@ public final class CollectionDetailReactor: Reactor { let item = currentState.collection.recentBookmarks[index] guard let type = item.type.toDictionaryType else { return .empty() } return .just(.navigateTo(.detail(type, item.originalId))) + case .deleteCollection: + return deleteCollectionUseCase.execute(collectionId: currentState.collection.collectionId) + .andThen(.just(.navigateTo(.dismiss))) } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift index f6256e61..c30e3e90 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailViewController.swift @@ -172,7 +172,7 @@ extension CollectionDetailViewController { ctaText: "삭제하기", cancelText: "취소", ctaAction: { - // 삭제 처리 + reactor.action.onNext(.deleteCollection) }, cancelAction: {} ) @@ -186,6 +186,7 @@ extension CollectionDetailViewController { .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } .withUnretained(self) + .observe(on: MainScheduler.instance) .subscribe { owner, route in switch route { case .dismiss: From 40be770da69d70692ee793013396ae3feb00ab08 Mon Sep 17 00:00:00 2001 From: p2glet Date: Thu, 27 Nov 2025 01:29:09 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat/#269:=20=EC=BB=AC=EB=A0=89=EC=85=98-?= =?UTF-8?q?=EB=B6=81=EB=A7=88=ED=81=AC=20=EC=B6=94=EA=B0=80=20API=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Endpoints/CollectionEndPoint.swift | 11 +++- .../Endpoints/DictionaryListEndPoint.swift | 4 +- .../CollectionAPIRepositoryImpl.swift | 52 +++++++++++-------- .../AddBookmarksToCollectionUseCaseImpl.swift | 30 +++++------ .../AddCollectionAndBookmarkUseCaseImpl.swift | 14 +++++ .../AddCollectionsToBookmarkUseCaseImpl.swift | 28 +++++----- .../Repository/CollectionAPIRepository.swift | 10 ++-- .../AddBookmarksToCollectionUseCase.swift | 10 ++-- .../AddCollectionAndBookmarkUseCase.swift | 5 ++ .../AddCollectionsToBookmarkUseCase.swift | 10 ++-- MLS/MLS/Application/AppDelegate.swift | 25 +++++---- .../BaseFeature/SharedView/BaseListView.swift | 6 +++ .../BookmarkListFactoryImpl.swift | 12 +++-- .../BookmarkList/BookmarkListReactor.swift | 32 +++++++++--- .../BookmarkListViewController.swift | 16 ++++-- .../BookmarkModalFactoryImpl.swift | 14 ++--- .../BookmarkModal/BookmarkModalReactor.swift | 16 +++--- .../CollectionDetailFactoryImpl.swift | 8 +-- .../CollectionDetailReactor.swift | 8 +-- .../CollectionEditViewController.swift | 3 +- .../BookmarkModalFactory.swift | 4 +- .../DictionaryDetailBaseViewController.swift | 2 +- .../DictionaryListReactor.swift | 12 ++--- .../DictionaryListViewController.swift | 2 +- 24 files changed, 207 insertions(+), 127 deletions(-) create mode 100644 MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionAndBookmarkUseCaseImpl.swift create mode 100644 MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionAndBookmarkUseCase.swift diff --git a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift index ab39e596..bc75553f 100644 --- a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift @@ -45,7 +45,7 @@ public enum CollectionEndPoint { body: body ) } - + public static func deleteCollection(id: Int) -> EndPoint { .init( baseURL: base, @@ -53,4 +53,13 @@ public enum CollectionEndPoint { method: .DELETE ) } + + public static func addCollectionAndBookmark(body: Encodable) -> EndPoint { + .init( + baseURL: base, + path: "/api/v1/bookmark-collections", + method: .POST, + body: body + ) + } } diff --git a/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift b/MLS/Data/Data/Network/Endpoints/DictionaryListEndPoint.swift index 80c81a2d..1f31fedb 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, maxLevel: maxLevel) + let query = DictionaryListQuery(keyword: keyword ?? "", page: page, size: size, sort: sort, minLevel: minLevel ?? 1, maxLevel: maxLevel ?? 200) return .init(baseURL: base, path: "/api/v1/monsters", method: .GET, query: query ) } @@ -47,7 +47,7 @@ public enum DictionaryListEndPoint { 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) + let query = DictionaryListQuery(keyword: keyword, page: page ?? 0, size: size ?? 20, sort: sort, minLevel: minLevel ?? 1, maxLevel: maxLevel ?? 200, jobIds: joinedJobIds, categoryIds: joinedCategoryIds) return .init(baseURL: base, path: "/api/v1/items", method: .GET, query: query ) } diff --git a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift index abc3e46b..75ef08ff 100644 --- a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift +++ b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift @@ -31,27 +31,27 @@ public class CollectionAPIRepositoryImpl: CollectionAPIRepository { .map { $0.toDomain() } } - public func addBookmarksToCollection(collectionId: Int, bookmarkIds: [Int]) -> Completable { - let endPoint = CollectionEndPoint.addBookmarksToCollection(id: collectionId, body: AddBookmarkRequestBody(bookmarkIds: bookmarkIds)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - .catch { error in - if let netErr = error as? NetworkError { - switch netErr { - case let .statusError(code, body): - return .error(DomainHTTPError.httpStatus(code: code, message: body)) - default: - return .error(DomainHTTPError.unknown) - } - } else { - return .error(DomainHTTPError.unknown) - } - } - } - - public func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable { - let endPoint = CollectionEndPoint.addCollectionsToBookmark(id: bookmarkId, body: AddCollectionRequestBody(collectionIds: collectionIds)) - return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) - } +// public func addBookmarksToCollection(collectionId: Int, bookmarkIds: [Int]) -> Completable { +// let endPoint = CollectionEndPoint.addBookmarksToCollection(id: collectionId, body: AddBookmarkRequestBody(bookmarkIds: bookmarkIds)) +// return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) +// .catch { error in +// if let netErr = error as? NetworkError { +// switch netErr { +// case let .statusError(code, body): +// return .error(DomainHTTPError.httpStatus(code: code, message: body)) +// default: +// return .error(DomainHTTPError.unknown) +// } +// } else { +// return .error(DomainHTTPError.unknown) +// } +// } +// } +// +// public func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable { +// let endPoint = CollectionEndPoint.addCollectionsToBookmark(id: bookmarkId, body: AddCollectionRequestBody(collectionIds: collectionIds)) +// return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) +// } public func setCollectionName(collectionId: Int, name: String) -> Completable { let endPoint = CollectionEndPoint.setCollectionName(id: collectionId, body: SetCollectionRequestBody(name: name)) @@ -62,6 +62,11 @@ public class CollectionAPIRepositoryImpl: CollectionAPIRepository { let endPoint = CollectionEndPoint.deleteCollection(id: collectionId) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) } + + public func addCollectionAndBookmark(collectionIds: [Int], bookmarkIds: [Int]) -> Completable { + let endPoint = CollectionEndPoint.addCollectionAndBookmark(body: AddCollectionAndBookmarkBody(collectionIds: collectionIds, bookmarkIds: bookmarkIds)) + return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) + } } private extension CollectionAPIRepositoryImpl { @@ -80,4 +85,9 @@ private extension CollectionAPIRepositoryImpl { struct SetCollectionRequestBody: Encodable { let name: String } + + struct AddCollectionAndBookmarkBody: Encodable { + let collectionIds: [Int] + let bookmarkIds: [Int] + } } diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift index b5e1c62a..9a3ecb18 100644 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift @@ -1,15 +1,15 @@ -import DomainInterface - -import RxSwift - -public final class AddBookmarksToCollectionUseCaseImpl: AddBookmarksToCollectionUseCase { - private let repository: CollectionAPIRepository - - public init(repository: CollectionAPIRepository) { - self.repository = repository - } - - public func execute(collectionId: Int, bookmarkIds: [Int]) -> Completable { - return repository.addBookmarksToCollection(collectionId: collectionId, bookmarkIds: bookmarkIds) - } -} +//import DomainInterface +// +//import RxSwift +// +//public final class AddBookmarksToCollectionUseCaseImpl: AddBookmarksToCollectionUseCase { +// private let repository: CollectionAPIRepository +// +// public init(repository: CollectionAPIRepository) { +// self.repository = repository +// } +// +// public func execute(collectionId: Int, bookmarkIds: [Int]) -> Completable { +// return repository.addBookmarksToCollection(collectionId: collectionId, bookmarkIds: bookmarkIds) +// } +//} diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionAndBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionAndBookmarkUseCaseImpl.swift new file mode 100644 index 00000000..55b640e6 --- /dev/null +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionAndBookmarkUseCaseImpl.swift @@ -0,0 +1,14 @@ +import DomainInterface +import RxSwift + +public final class AddCollectionAndBookmarkUseCaseImpl: AddCollectionAndBookmarkUseCase { + private let repository: CollectionAPIRepository + + public init(repository: CollectionAPIRepository) { + self.repository = repository + } + + public func execute(collectionIds: [Int], bookmarkIds: [Int]) -> Completable { + return repository.addCollectionAndBookmark(collectionIds: collectionIds, bookmarkIds: bookmarkIds) + } +} diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift index 456763f9..3e1eda24 100644 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift @@ -1,14 +1,14 @@ -import DomainInterface -import RxSwift - -public final class AddCollectionsToBookmarkUseCaseImpl: AddCollectionsToBookmarkUseCase { - private let repository: CollectionAPIRepository - - public init(repository: CollectionAPIRepository) { - self.repository = repository - } - - public func execute(bookmarkId: Int, collectionIds: [Int]) -> Completable { - return repository.addCollectionsToBookmark(bookmarkId: bookmarkId, collectionIds: collectionIds) - } -} +//import DomainInterface +//import RxSwift +// +//public final class AddCollectionsToBookmarkUseCaseImpl: AddCollectionsToBookmarkUseCase { +// private let repository: CollectionAPIRepository +// +// public init(repository: CollectionAPIRepository) { +// self.repository = repository +// } +// +// public func execute(bookmarkId: Int, collectionIds: [Int]) -> Completable { +// return repository.addCollectionsToBookmark(bookmarkId: bookmarkId, collectionIds: collectionIds) +// } +//} diff --git a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift index 0a041212..38f62e88 100644 --- a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift +++ b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift @@ -10,11 +10,13 @@ public protocol CollectionAPIRepository { // 컬렉션 상세 조회 func fetchCollectionUseCase(id: Int) -> Observable<[BookmarkResponse]> - func addBookmarksToCollection(collectionId: Int, bookmarkIds: [Int]) -> Completable +// func addBookmarksToCollection(collectionId: Int, bookmarkIds: [Int]) -> Completable + +// func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable - func addCollectionsToBookmark(bookmarkId: Int, collectionIds: [Int]) -> Completable - func setCollectionName(collectionId: Int, name: String) -> Completable - + func deleteCollection(collectionId: Int) -> Completable + + func addCollectionAndBookmark(collectionIds: [Int], bookmarkIds: [Int]) -> Completable } diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift index e2ecb8d2..ff3d6d64 100644 --- a/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift +++ b/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift @@ -1,5 +1,5 @@ -import RxSwift - -public protocol AddBookmarksToCollectionUseCase { - func execute(collectionId: Int, bookmarkIds: [Int]) -> Completable -} +//import RxSwift +// +//public protocol AddBookmarksToCollectionUseCase { +// func execute(collectionId: Int, bookmarkIds: [Int]) -> Completable +//} diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionAndBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionAndBookmarkUseCase.swift new file mode 100644 index 00000000..b8f641d4 --- /dev/null +++ b/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionAndBookmarkUseCase.swift @@ -0,0 +1,5 @@ +import RxSwift + +public protocol AddCollectionAndBookmarkUseCase { + func execute(collectionIds: [Int], bookmarkIds: [Int]) -> Completable +} diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift index 1a63fe31..ce668445 100644 --- a/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift +++ b/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift @@ -1,5 +1,5 @@ -import RxSwift - -public protocol AddCollectionsToBookmarkUseCase { - func execute(bookmarkId: Int, collectionIds: [Int]) -> Completable -} +//import RxSwift +// +//public protocol AddCollectionsToBookmarkUseCase { +// func execute(bookmarkId: Int, collectionIds: [Int]) -> Completable +//} diff --git a/MLS/MLS/Application/AppDelegate.swift b/MLS/MLS/Application/AppDelegate.swift index 04c25d28..86e4cbf4 100644 --- a/MLS/MLS/Application/AppDelegate.swift +++ b/MLS/MLS/Application/AppDelegate.swift @@ -510,17 +510,20 @@ extension AppDelegate { DIContainer.register(type: FetchCollectionUseCase.self) { FetchCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) } - DIContainer.register(type: AddCollectionsToBookmarkUseCase.self) { - AddCollectionsToBookmarkUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) - } +// DIContainer.register(type: AddCollectionsToBookmarkUseCase.self) { +// AddCollectionsToBookmarkUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) +// } DIContainer.register(type: SetCollectionUseCase.self) { SetCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) } DIContainer.register(type: DeleteCollectionUseCase.self) { DeleteCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) } - DIContainer.register(type: AddBookmarksToCollectionUseCase.self) { - AddBookmarksToCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) +// DIContainer.register(type: AddBookmarksToCollectionUseCase.self) { +// AddBookmarksToCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) +// } + DIContainer.register(type: AddCollectionAndBookmarkUseCase.self) { + AddCollectionAndBookmarkUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) } } @@ -540,7 +543,9 @@ extension AppDelegate { DIContainer.register(type: BookmarkModalFactory.self) { BookmarkModalFactoryImpl( addCollectionFactory: DIContainer.resolve( - type: AddCollectionFactory.self), fetchCollectionListUseCase: DIContainer.resolve(type: FetchCollectionListUseCase.self), addCollectionsToBookmarkUseCase: DIContainer.resolve(type: AddCollectionsToBookmarkUseCase.self)) + type: AddCollectionFactory.self), fetchCollectionListUseCase: DIContainer.resolve(type: FetchCollectionListUseCase.self), + addCollectionAndBookmarkUseCase: DIContainer.resolve(type: AddCollectionAndBookmarkUseCase.self) + ) } DIContainer.register(type: LoginFactory.self) { LoginFactoryImpl( @@ -781,6 +786,8 @@ extension AppDelegate { loginFactory: DIContainer.resolve(type: LoginFactory.self), dictionaryDetailFactory: DIContainer.resolve( type: DictionaryDetailFactory.self), + collectionEditFactory: DIContainer.resolve( + type: CollectionEditFactory.self), setBookmarkUseCase: DIContainer.resolve( type: SetBookmarkUseCase.self), checkLoginUseCase: DIContainer.resolve( @@ -797,8 +804,7 @@ extension AppDelegate { type: FetchQuestBookmarkUseCase.self), fetchMapBookmarkUseCase: DIContainer.resolve( type: FetchMapBookmarkUseCase.self), - collectionEditFactory: DIContainer.resolve( - type: CollectionEditFactory.self)) + parseItemFilterResultUseCase: DIContainer.resolve(type: ParseItemFilterResultUseCase.self)) } DIContainer.register(type: CollectionListFactory.self) { CollectionListFactoryImpl( @@ -833,8 +839,7 @@ extension AppDelegate { type: FetchCollectionUseCase.self), deleteCollectionUseCase: DIContainer .resolve(type: DeleteCollectionUseCase.self), - addBookmarksToCollectionUseCase: DIContainer - .resolve(type: AddBookmarksToCollectionUseCase.self) + addCollectionAndBookmarkUseCase: DIContainer.resolve(type: AddCollectionAndBookmarkUseCase.self) ) } DIContainer.register(type: CollectionSettingFactory.self) { diff --git a/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift b/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift index d2aa8973..5bee442c 100644 --- a/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift +++ b/MLS/Presentation/BaseFeature/BaseFeature/SharedView/BaseListView.swift @@ -128,6 +128,12 @@ public extension BaseListView { } } + func updateBookmarkFilter(type: DictionaryType) { + if type == .total { + filterButton.isHidden = true + } + } + static func makeSortButton(title: String, tintColor: UIColor) -> UIButton { let button = UIButton() button.setAttributedTitle(.makeStyledString(font: .b_s_r, text: title), for: .normal) diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListFactoryImpl.swift index dec82dda..74661da1 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListFactoryImpl.swift @@ -11,6 +11,7 @@ public final class BookmarkListFactoryImpl: BookmarkListFactory { private let bookmarkModalFactory: BookmarkModalFactory private let loginFactory: LoginFactory private let dictionaryDetailFactory: DictionaryDetailFactory + private let collectionEditFactory: CollectionEditFactory private let setBookmarkUseCase: SetBookmarkUseCase private let checkLoginUseCase: CheckLoginUseCase @@ -20,7 +21,7 @@ public final class BookmarkListFactoryImpl: BookmarkListFactory { private let fetchNPCBookmarkUseCase: FetchNPCBookmarkUseCase private let fetchQuestBookmarkUseCase: FetchQuestBookmarkUseCase private let fetchMapBookmarkUseCase: FetchMapBookmarkUseCase - private let collectionEditFactory: CollectionEditFactory + private let parseItemFilterResultUseCase: ParseItemFilterResultUseCase public init( itemFilterFactory: ItemFilterBottomSheetFactory, @@ -29,6 +30,7 @@ public final class BookmarkListFactoryImpl: BookmarkListFactory { bookmarkModalFactory: BookmarkModalFactory, loginFactory: LoginFactory, dictionaryDetailFactory: DictionaryDetailFactory, + collectionEditFactory: CollectionEditFactory, setBookmarkUseCase: SetBookmarkUseCase, checkLoginUseCase: CheckLoginUseCase, fetchBookmarkUseCase: FetchBookmarkUseCase, @@ -37,7 +39,7 @@ public final class BookmarkListFactoryImpl: BookmarkListFactory { fetchNPCBookmarkUseCase: FetchNPCBookmarkUseCase, fetchQuestBookmarkUseCase: FetchQuestBookmarkUseCase, fetchMapBookmarkUseCase: FetchMapBookmarkUseCase, - collectionEditFactory: CollectionEditFactory + parseItemFilterResultUseCase: ParseItemFilterResultUseCase ) { self.itemFilterFactory = itemFilterFactory self.monsterFilterFactory = monsterFilterFactory @@ -45,6 +47,7 @@ public final class BookmarkListFactoryImpl: BookmarkListFactory { self.bookmarkModalFactory = bookmarkModalFactory self.loginFactory = loginFactory self.dictionaryDetailFactory = dictionaryDetailFactory + self.collectionEditFactory = collectionEditFactory self.setBookmarkUseCase = setBookmarkUseCase self.checkLoginUseCase = checkLoginUseCase self.fetchBookmarkUseCase = fetchBookmarkUseCase @@ -53,7 +56,7 @@ public final class BookmarkListFactoryImpl: BookmarkListFactory { self.fetchItemBookmarkUseCase = fetchItemBookmarkUseCase self.fetchQuestBookmarkUseCase = fetchQuestBookmarkUseCase self.fetchMapBookmarkUseCase = fetchMapBookmarkUseCase - self.collectionEditFactory = collectionEditFactory + self.parseItemFilterResultUseCase = parseItemFilterResultUseCase } public func make(type: DictionaryType, listType: DictionaryMainViewType) -> BaseViewController { @@ -66,7 +69,8 @@ public final class BookmarkListFactoryImpl: BookmarkListFactory { fetchItemBookmarkUseCase: fetchItemBookmarkUseCase, fetchNPCBookmarkUseCase: fetchNPCBookmarkUseCase, fetchQuestBookmarkUseCase: fetchQuestBookmarkUseCase, - fetchMapBookmarkUseCase: fetchMapBookmarkUseCase + fetchMapBookmarkUseCase: fetchMapBookmarkUseCase, + parseItemFilterResultUseCase: parseItemFilterResultUseCase ) let viewController = BookmarkListViewController( reactor: reactor, diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListReactor.swift index 795752fa..6ca97d98 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListReactor.swift @@ -34,6 +34,7 @@ public final class BookmarkListReactor: Reactor { case undoLastDeletedBookmark case dataTapped(Int) case emptyButtonTapped + case itemFilterOptionSelected([(String, String)]) } // MARK: - Mutation @@ -41,9 +42,11 @@ public final class BookmarkListReactor: Reactor { case setItems([BookmarkResponse]) case setLoginState(Bool) case setSort(SortType) - case setFilter(start: Int, end: Int) + case setFilter(start: Int?, end: Int?) case setLastDeletedBookmark(BookmarkResponse?) case toNavagate(Route) + case setJobId([Int]) + case setCategoryId([Int]) } // MARK: - State @@ -52,6 +55,8 @@ public final class BookmarkListReactor: Reactor { var items: [BookmarkResponse] = [] var type: DictionaryType var isLogin: Bool + var jobId: [Int]? + var categoryIds: [Int]? var sort: SortType? var startLevel: Int? var endLevel: Int? @@ -79,6 +84,7 @@ public final class BookmarkListReactor: Reactor { private let fetchNPCBookmarkUseCase: FetchNPCBookmarkUseCase private let fetchQuestBookmarkUseCase: FetchQuestBookmarkUseCase private let fetchMapBookmarkUseCase: FetchMapBookmarkUseCase + private let parseItemFilterResultUseCase: ParseItemFilterResultUseCase private let disposeBag = DisposeBag() @@ -92,7 +98,8 @@ public final class BookmarkListReactor: Reactor { fetchItemBookmarkUseCase: FetchItemBookmarkUseCase, fetchNPCBookmarkUseCase: FetchNPCBookmarkUseCase, fetchQuestBookmarkUseCase: FetchQuestBookmarkUseCase, - fetchMapBookmarkUseCase: FetchMapBookmarkUseCase + fetchMapBookmarkUseCase: FetchMapBookmarkUseCase, + parseItemFilterResultUseCase: ParseItemFilterResultUseCase ) { self.initialState = State(route: .none, type: type, isLogin: false) self.checkLoginUseCase = checkLoginUseCase @@ -103,6 +110,7 @@ public final class BookmarkListReactor: Reactor { self.fetchNPCBookmarkUseCase = fetchNPCBookmarkUseCase self.fetchQuestBookmarkUseCase = fetchQuestBookmarkUseCase self.fetchMapBookmarkUseCase = fetchMapBookmarkUseCase + self.parseItemFilterResultUseCase = parseItemFilterResultUseCase } // MARK: - Mutate @@ -184,6 +192,18 @@ public final class BookmarkListReactor: Reactor { } case .editButtonTapped: return .just(.toNavagate(.edit)) + 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)) + ]) + .concat(Observable.deferred { [weak self] in + guard let self = self else { return .empty() } + return self.fetchList() + }) } } @@ -235,21 +255,21 @@ public final class BookmarkListReactor: Reactor { switch mutation { case let .setItems(response): newState.items = response - case let .setLoginState(isLogin): newState.isLogin = isLogin - case let .setSort(sort): newState.sort = sort - case let .setFilter(start, end): newState.startLevel = start newState.endLevel = end - case let .setLastDeletedBookmark(item): newState.lastDeletedBookmark = item case .toNavagate(let route): newState.route = route + case .setJobId(let ids): + newState.jobId = ids + case .setCategoryId(let ids): + newState.categoryIds = ids } return newState diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift index 2bec443e..b76516fc 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkList/BookmarkListViewController.swift @@ -157,12 +157,19 @@ extension BookmarkListViewController { case .filter(let type): switch type { case .item: - break - // let viewController = owner.itemFilterFactory.make() - // owner.present(viewController, animated: true) + 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 - + owner.mainView.selectFilter() reactor.action.onNext(.filterOptionSelected(startLevel: startLevel, endLevel: endLevel)) } owner.tabBarController?.presentModal(viewController) @@ -194,6 +201,7 @@ extension BookmarkListViewController { .distinctUntilChanged() .withUnretained(self) .bind(onNext: { owner, type in + owner.mainView.updateBookmarkFilter(type: type) owner.mainView.updateFilter(sortType: type.bookmarkSortedFilter.first) }) .disposed(by: disposeBag) diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift index 878fe766..669604f3 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift @@ -5,23 +5,23 @@ import DomainInterface public final class BookmarkModalFactoryImpl: BookmarkModalFactory { private let addCollectionFactory: AddCollectionFactory private let fetchCollectionListUseCase: FetchCollectionListUseCase - private let addCollectionsToBookmarkUseCase: AddCollectionsToBookmarkUseCase + private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase - public init(addCollectionFactory: AddCollectionFactory, fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionsToBookmarkUseCase: AddCollectionsToBookmarkUseCase) { + public init(addCollectionFactory: AddCollectionFactory, fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase) { self.addCollectionFactory = addCollectionFactory self.fetchCollectionListUseCase = fetchCollectionListUseCase - self.addCollectionsToBookmarkUseCase = addCollectionsToBookmarkUseCase + self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase } - public func make(bookmarkId: Int) -> BaseViewController { - let reactor = BookmarkModalReactor(bookmarkId: bookmarkId, fetchCollectionListUseCase: fetchCollectionListUseCase, addCollectionsToBookmarkUseCase: addCollectionsToBookmarkUseCase) + public func make(bookmarkIds: [Int]) -> BaseViewController { + let reactor = BookmarkModalReactor(bookmarkIds: bookmarkIds, fetchCollectionListUseCase: fetchCollectionListUseCase, addCollectionAndBookmarkUseCase: addCollectionAndBookmarkUseCase) let viewController = BookmarkModalViewController(addCollectionFactory: addCollectionFactory) viewController.reactor = reactor return viewController } - public func make(bookmarkId: Int, onComplete: ((Bool) -> Void)? = nil) -> BaseViewController { - let viewController = make(bookmarkId: bookmarkId) + public func make(bookmarkIds: [Int], onComplete: ((Bool) -> Void)? = nil) -> BaseViewController { + let viewController = make(bookmarkIds: bookmarkIds) if let viewController = viewController as? BookmarkModalViewController { viewController.onCompleted = onComplete } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift index 38a71be3..4c84e236 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift @@ -29,7 +29,7 @@ public final class BookmarkModalReactor: Reactor { public struct State { @Pulse var route: Route - var bookmarkId: Int + var bookmarkIds: [Int] var collections = [CollectionResponse]() var selectedItems = [CollectionResponse]() } @@ -39,12 +39,12 @@ public final class BookmarkModalReactor: Reactor { private let disposeBag = DisposeBag() private let fetchCollectionListUseCase: FetchCollectionListUseCase - private let addCollectionsToBookmarkUseCase: AddCollectionsToBookmarkUseCase + private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase - public init(bookmarkId: Int, fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionsToBookmarkUseCase: AddCollectionsToBookmarkUseCase) { - self.initialState = State(route: .none, bookmarkId: bookmarkId) + public init(bookmarkIds: [Int], fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase) { + self.initialState = State(route: .none, bookmarkIds: bookmarkIds) self.fetchCollectionListUseCase = fetchCollectionListUseCase - self.addCollectionsToBookmarkUseCase = addCollectionsToBookmarkUseCase + self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase } public func mutate(action: Action) -> Observable { @@ -54,10 +54,10 @@ public final class BookmarkModalReactor: Reactor { .map { .setCollection($0) } case .addButtonTapped: - return addCollectionsToBookmarkUseCase + return addCollectionAndBookmarkUseCase .execute( - bookmarkId: currentState.bookmarkId, - collectionIds: currentState.selectedItems.map { $0.collectionId } + collectionIds: currentState.selectedItems.map { $0.collectionId }, + bookmarkIds: currentState.bookmarkIds ) .do(onError: { error in if let error = error as? DomainHTTPError { diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift index a9051cc4..83341f3f 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift @@ -16,7 +16,7 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { private let setBookmarkUseCase: SetBookmarkUseCase private let fetchCollectionUseCase: FetchCollectionUseCase private let deleteCollectionUseCase: DeleteCollectionUseCase - private let addBookmarksToCollectionUseCase: AddBookmarksToCollectionUseCase + private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase public init( bookmarkModalFactory: BookmarkModalFactory, @@ -27,7 +27,7 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { setBookmarkUseCase: SetBookmarkUseCase, fetchCollectionUseCase: FetchCollectionUseCase, deleteCollectionUseCase: DeleteCollectionUseCase, - addBookmarksToCollectionUseCase: AddBookmarksToCollectionUseCase + addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase ) { self.bookmarkModalFactory = bookmarkModalFactory self.collectionSettingFactory = collectionSettingFactory @@ -37,7 +37,7 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { self.setBookmarkUseCase = setBookmarkUseCase self.fetchCollectionUseCase = fetchCollectionUseCase self.deleteCollectionUseCase = deleteCollectionUseCase - self.addBookmarksToCollectionUseCase = addBookmarksToCollectionUseCase + self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase } public func make(collection: CollectionResponse, onMoveToMain: (() -> Void)?) -> BaseViewController { @@ -46,7 +46,7 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { setBookmarkUseCase: setBookmarkUseCase, fetchCollectionUseCase: fetchCollectionUseCase, deleteCollectionUseCase: deleteCollectionUseCase, - addBookmarksToCollectionUseCase: addBookmarksToCollectionUseCase + addCollectionAndBookmarkUseCase: addCollectionAndBookmarkUseCase ) let viewController = CollectionDetailViewController( diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift index fd0828f3..1df67134 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailReactor.swift @@ -49,18 +49,18 @@ public final class CollectionDetailReactor: Reactor { private let setBookmarkUseCase: SetBookmarkUseCase private let fetchCollectionUseCase: FetchCollectionUseCase private let deleteCollectionUseCase: DeleteCollectionUseCase - private let addBookmarksToCollectionUseCase: AddBookmarksToCollectionUseCase + private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase public var initialState: State private let disposeBag = DisposeBag() - public init(collection: CollectionResponse, setBookmarkUseCase: SetBookmarkUseCase, fetchCollectionUseCase: FetchCollectionUseCase, deleteCollectionUseCase: DeleteCollectionUseCase, addBookmarksToCollectionUseCase: AddBookmarksToCollectionUseCase) { + public init(collection: CollectionResponse, setBookmarkUseCase: SetBookmarkUseCase, fetchCollectionUseCase: FetchCollectionUseCase, deleteCollectionUseCase: DeleteCollectionUseCase, addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase) { self.initialState = State(route: .none, collection: collection) self.setBookmarkUseCase = setBookmarkUseCase self.fetchCollectionUseCase = fetchCollectionUseCase self.deleteCollectionUseCase = deleteCollectionUseCase - self.addBookmarksToCollectionUseCase = addBookmarksToCollectionUseCase + self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase } public func mutate(action: Action) -> Observable { @@ -103,7 +103,7 @@ public final class CollectionDetailReactor: Reactor { isBookmark: .set(lastDeleted.type) ) // 북마크 다시 설정시 이전 collection을 전부 추적해야하고 새로 바뀐 북마크ID가 필요하여 현재는 원할하게 동작하지 않음 - .andThen(addBookmarksToCollectionUseCase.execute(collectionId: currentState.collection.collectionId, bookmarkIds: [lastDeletedBookmarkId])) + .andThen(addCollectionAndBookmarkUseCase.execute(collectionIds: [currentState.collection.collectionId], bookmarkIds: [lastDeletedBookmarkId])) .andThen( Observable.concat([ fetchCollectionUseCase.execute(id: currentState.collection.collectionId) diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift index f85d8586..2a0627c7 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionEdit/CollectionEditViewController.swift @@ -127,8 +127,7 @@ extension CollectionEditViewController { case .dismiss: owner.navigationController?.popViewController(animated: true) case .collcectionList: - // 수정필요 bookmarkId -> bookmarkIds - let viewController = owner.bookmarkModalFactory.make(bookmarkId: reactor.currentState.selectedItems.first?.bookmarkId ?? 0) + let viewController = owner.bookmarkModalFactory.make(bookmarkIds: reactor.currentState.selectedItems.map { $0.bookmarkId }) owner.present(viewController, animated: true) default: break diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift index efee86e8..8c96281c 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeatureInterface/BookmarkModalFactory.swift @@ -2,6 +2,6 @@ import BaseFeature import DomainInterface public protocol BookmarkModalFactory { - func make(bookmarkId: Int) -> BaseViewController - func make(bookmarkId: Int, onComplete: ((Bool) -> Void)?) -> BaseViewController + func make(bookmarkIds: [Int]) -> BaseViewController + func make(bookmarkIds: [Int], onComplete: ((Bool) -> Void)?) -> BaseViewController } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift index ad4defc8..7b65a1e8 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift @@ -298,7 +298,7 @@ extension DictionaryDetailBaseViewController { buttonAction: { [weak self] in guard let self, let id = bookmarkId else { return } - let viewController = self.bookmarkModalFactory.make(bookmarkId: id, onComplete: { isAdd in + let viewController = self.bookmarkModalFactory.make(bookmarkIds: [id], onComplete: { isAdd in if isAdd { DispatchQueue.main.async { ToastFactory.createToast( diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift index e1f8c2c6..621a2c83 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListReactor.swift @@ -50,12 +50,10 @@ open class DictionaryListReactor: Reactor { public var keyword: String? public var jobId: [Int]? - public var minLevel: Int? - public var maxLevel: Int? public var categoryIds: [Int]? public var sort: String? - public var startLevel: Int? = 1 - public var endLevel: Int? = 200 + public var startLevel: Int? + public var endLevel: Int? public var currentPage = 0 public var totalCounts = 0 @@ -152,7 +150,7 @@ open class DictionaryListReactor: Reactor { } } - // MARK: - Fetch (완전 통합) + // MARK: - Fetch private func fetchList( sort: String?, startLevel: Int?, @@ -179,8 +177,8 @@ open class DictionaryListReactor: Reactor { response = dictionaryItemListUseCase.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, diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift index fc28b1d6..c1700ee7 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryList/DictionaryListViewController.swift @@ -272,7 +272,7 @@ extension DictionaryListViewController: UICollectionViewDelegate, UICollectionVi let reactor = self.reactor, let id = reactor.currentState.listItems[indexPath.row].bookmarkId else { return } - let viewController = self.bookmarkModalFactory.make(bookmarkId: id, onComplete: { isAdd in + let viewController = self.bookmarkModalFactory.make(bookmarkIds: [id], onComplete: { isAdd in if isAdd { ToastFactory.createToast( message: From 58598f330b0e44fbf807d9cd0e84186bb419aec7 Mon Sep 17 00:00:00 2001 From: p2glet Date: Thu, 27 Nov 2025 01:46:02 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat/#269:=20=EC=BB=AC=EB=A0=89=EC=85=98?= =?UTF-8?q?=ED=83=AD=20sortFilter=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Endpoints/CollectionEndPoint.swift | 9 ++++++-- .../CollectionAPIRepositoryImpl.swift | 8 +++++-- .../FetchCollectionListUseCaseImpl.swift | 4 ++-- .../Repository/CollectionAPIRepository.swift | 2 +- .../FetchCollectionListUseCase.swift | 2 +- MLS/MLS/Application/AppDelegate.swift | 4 +++- .../BookmarkModal/BookmarkModalReactor.swift | 2 +- .../CollectionListFactoryImpl.swift | 7 +++++-- .../CollectionListReactor.swift | 19 +++++++++++++++-- .../CollectionList/CollectionListView.swift | 5 +++++ .../CollectionListViewController.swift | 21 +++++++++++++++++-- 11 files changed, 67 insertions(+), 16 deletions(-) diff --git a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift index bc75553f..59f65278 100644 --- a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift @@ -3,8 +3,13 @@ import DomainInterface public enum CollectionEndPoint { static let base = "https://api.mapleland.kro.kr" - public static func fetchCollectionList() -> ResponsableEndPoint<[CollectionListResponseDTO]> { - .init(baseURL: base, path: "/api/v1/collections", method: .GET) + public static func fetchCollectionList(query: Encodable) -> ResponsableEndPoint<[CollectionListResponseDTO]> { + .init( + baseURL: base, + path: "/api/v1/collections", + method: .GET, + query: query + ) } public static func createCollectionList(body: Encodable) -> EndPoint { diff --git a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift index 75ef08ff..94b24adf 100644 --- a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift +++ b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift @@ -13,8 +13,8 @@ public class CollectionAPIRepositoryImpl: CollectionAPIRepository { self.tokenInterceptor = tokenInterceptor } - public func fetchCollectionList() -> Observable<[CollectionResponse]> { - let endPoint = CollectionEndPoint.fetchCollectionList() + public func fetchCollectionList(sort: String?) -> Observable<[CollectionResponse]> { + let endPoint = CollectionEndPoint.fetchCollectionList(query: FetchCollectionListQuery(sort: sort)) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) .map { $0.map { $0.toDomain() } } @@ -70,6 +70,10 @@ public class CollectionAPIRepositoryImpl: CollectionAPIRepository { } private extension CollectionAPIRepositoryImpl { + struct FetchCollectionListQuery: Encodable { + let sort: String? + } + struct CreateCollectionRequestDTO: Encodable { let name: String } diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/FetchCollectionListUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/FetchCollectionListUseCaseImpl.swift index 8e7793d1..a1f88c48 100644 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/FetchCollectionListUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/FetchCollectionListUseCaseImpl.swift @@ -9,7 +9,7 @@ public final class FetchCollectionListUseCaseImpl: FetchCollectionListUseCase { self.repository = repository } - public func execute() -> Observable<[CollectionResponse]> { - return repository.fetchCollectionList() + public func execute(sort: SortType?) -> Observable<[CollectionResponse]> { + return repository.fetchCollectionList(sort: sort?.sortParameter) } } diff --git a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift index 38f62e88..bf23db9b 100644 --- a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift +++ b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift @@ -4,7 +4,7 @@ import RxSwift public protocol CollectionAPIRepository { // 컬렉션 목록 조회 - func fetchCollectionList() -> Observable<[CollectionResponse]> + func fetchCollectionList(sort: String?) -> Observable<[CollectionResponse]> // 컬렉션 목록 추가 func createCollectionList(name: String) -> Completable // 컬렉션 상세 조회 diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/FetchCollectionListUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/FetchCollectionListUseCase.swift index 6b71f667..96513970 100644 --- a/MLS/Domain/DomainInterface/UseCase/Collection/FetchCollectionListUseCase.swift +++ b/MLS/Domain/DomainInterface/UseCase/Collection/FetchCollectionListUseCase.swift @@ -1,5 +1,5 @@ import RxSwift public protocol FetchCollectionListUseCase { - func execute() -> Observable<[CollectionResponse]> + func execute(sort: SortType?) -> Observable<[CollectionResponse]> } diff --git a/MLS/MLS/Application/AppDelegate.swift b/MLS/MLS/Application/AppDelegate.swift index 86e4cbf4..3312149e 100644 --- a/MLS/MLS/Application/AppDelegate.swift +++ b/MLS/MLS/Application/AppDelegate.swift @@ -813,7 +813,9 @@ extension AppDelegate { addCollectionFactory: DIContainer.resolve( type: AddCollectionFactory.self), bookmarkDetailFactory: DIContainer.resolve( - type: CollectionDetailFactory.self)) + type: CollectionDetailFactory.self), + sortedBottomSheetFactory: DIContainer + .resolve(type: SortedBottomSheetFactory.self)) } DIContainer.register(type: CollectionDetailFactory.self) { CollectionDetailFactoryImpl( diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift index 4c84e236..1f50c211 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift @@ -50,7 +50,7 @@ public final class BookmarkModalReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .viewWillAppear, .completeAdding: - return fetchCollectionListUseCase.execute() + return fetchCollectionListUseCase.execute(sort: nil) .map { .setCollection($0) } case .addButtonTapped: diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift index 074ff62a..6d0d0513 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift @@ -1,21 +1,24 @@ import BaseFeature import BookmarkFeatureInterface +import DictionaryFeatureInterface import DomainInterface public final class CollectionListFactoryImpl: CollectionListFactory { private let fetchCollectionListUseCase: FetchCollectionListUseCase private let addCollectionFactory: AddCollectionFactory private let bookmarkDetailFactory: CollectionDetailFactory + private let sortedBottomSheetFactory: SortedBottomSheetFactory - public init(fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionFactory: AddCollectionFactory, bookmarkDetailFactory: CollectionDetailFactory) { + public init(fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionFactory: AddCollectionFactory, bookmarkDetailFactory: CollectionDetailFactory, sortedBottomSheetFactory: SortedBottomSheetFactory) { self.fetchCollectionListUseCase = fetchCollectionListUseCase self.addCollectionFactory = addCollectionFactory self.bookmarkDetailFactory = bookmarkDetailFactory + self.sortedBottomSheetFactory = sortedBottomSheetFactory } public func make() -> BaseViewController { let reactor = CollectionListReactor(fetchCollectionListUseCase: fetchCollectionListUseCase) - let viewController = CollectionListViewController(addCollectionFactory: addCollectionFactory, detailFactory: bookmarkDetailFactory) + let viewController = CollectionListViewController(addCollectionFactory: addCollectionFactory, detailFactory: bookmarkDetailFactory, sortedBottomSheetFactory: sortedBottomSheetFactory) viewController.reactor = reactor return viewController } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift index f196bee0..e0f69150 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift @@ -9,22 +9,28 @@ public final class CollectionListReactor: Reactor { public enum Route { case none case detail(CollectionResponse) + case sortFilter } public enum Action { case itemTapped(Int) case viewWillAppear case completeAdding + case sortButtonTapped + case sortOptionSelected(SortType) } public enum Mutation { case navigateTo(Route) case setListData([CollectionResponse]) + case setSort(SortType) } public struct State { @Pulse var route: Route var collectionList: [CollectionResponse] + var sortFilter: [SortType] = [.korean, .latest] + var selectedSort: SortType? } // MARK: - Properties @@ -42,12 +48,19 @@ public final class CollectionListReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .viewWillAppear: - return fetchCollectionListUseCase.execute().map { .setListData($0) } + return fetchCollectionListUseCase.execute(sort: currentState.selectedSort).map { .setListData($0) } case .itemTapped(let index): return .just(.navigateTo(.detail(currentState.collectionList[index]))) case .completeAdding: - return fetchCollectionListUseCase.execute() + return fetchCollectionListUseCase.execute(sort: currentState.selectedSort) .map {.setListData($0)} + case .sortButtonTapped: + return .just(.navigateTo(.sortFilter)) + case .sortOptionSelected(let sort): + return Observable.concat([ + .just(.setSort(sort)), + fetchCollectionListUseCase.execute(sort: currentState.selectedSort).map { .setListData($0) } + ]) } } @@ -58,6 +71,8 @@ public final class CollectionListReactor: Reactor { newState.collectionList = data case .navigateTo(let route): newState.route = route + case .setSort(let sort): + newState.selectedSort = sort } return newState } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift index aa96f4d3..3881cc33 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift @@ -122,4 +122,9 @@ extension CollectionListView { sortButton.setAttributedTitle(.makeStyledString(font: .b_s_r, text: selectedType.rawValue, color: .primary700), for: .normal) sortButton.tintColor = .primary700 } + + func selectSort(selectedType: SortType) { + sortButton.setAttributedTitle(.makeStyledString(font: .b_s_r, text: selectedType.rawValue, color: .primary700), for: .normal) + sortButton.tintColor = .primary700 + } } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift index 6b0d8fd1..70520965 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift @@ -3,6 +3,7 @@ import UIKit import BaseFeature import BookmarkFeatureInterface import DesignSystem +import DictionaryFeatureInterface import DomainInterface import ReactorKit @@ -13,18 +14,21 @@ public final class CollectionListViewController: BaseViewController, View { // MARK: - Properties public var disposeBag = DisposeBag() + private var selectedSortIndex = 0 - public var onDismissWithMessage: ((CollectionResponse?) -> Void)? +// public var onDismissWithMessage: ((CollectionResponse?) -> Void)? private let addCollectionFactory: AddCollectionFactory private let detailFactory: CollectionDetailFactory + private let sortedBottomSheetFactory: SortedBottomSheetFactory // MARK: - Components private var mainView = CollectionListView() - public init(addCollectionFactory: AddCollectionFactory, detailFactory: CollectionDetailFactory) { + public init(addCollectionFactory: AddCollectionFactory, detailFactory: CollectionDetailFactory, sortedBottomSheetFactory: SortedBottomSheetFactory) { self.addCollectionFactory = addCollectionFactory self.detailFactory = detailFactory + self.sortedBottomSheetFactory = sortedBottomSheetFactory super.init() } @@ -97,6 +101,11 @@ extension CollectionListViewController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) + + mainView.sortButton.rx.tap + .map { .sortButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) } func bindViewState(reactor: Reactor) { @@ -114,6 +123,14 @@ extension CollectionListViewController { } }) owner.tabBarController?.navigationController?.pushViewController(viewController, animated: true) + case .sortFilter: + let viewController = owner.sortedBottomSheetFactory.make(sortedOptions: reactor.currentState.sortFilter, selectedIndex: owner.selectedSortIndex) { index in + owner.selectedSortIndex = index + let selectedFilter = reactor.currentState.sortFilter[index] + reactor.action.onNext(.sortOptionSelected(selectedFilter)) + owner.mainView.selectSort(selectedType: selectedFilter) + } + owner.tabBarController?.presentModal(viewController) default: break } From 4f47142b22b838d89385b4145f4d786b618dc973 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 27 Nov 2025 08:19:41 +0000 Subject: [PATCH 08/10] style/#269: Apply SwiftLint autocorrect --- MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift | 2 +- MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift | 2 +- .../Data/Repository/CollectionAPIRepositoryImpl.swift | 8 ++++---- .../Collection/AddBookmarksToCollectionUseCaseImpl.swift | 8 ++++---- .../Collection/AddCollectionsToBookmarkUseCaseImpl.swift | 8 ++++---- .../Repository/CollectionAPIRepository.swift | 2 +- .../Collection/AddBookmarksToCollectionUseCase.swift | 6 +++--- .../Collection/AddCollectionsToBookmarkUseCase.swift | 6 +++--- .../AddCollection/AddCollectionFactoryImpl.swift | 2 +- .../AddCollection/AddCollectionViewController.swift | 2 +- .../CollectionDetail/CollectionDetailView.swift | 2 +- .../CollectionList/CollectionListReactor.swift | 1 - .../CollectionList/CollectionListView.swift | 2 +- .../CollectionList/CollectionListViewController.swift | 2 +- 14 files changed, 26 insertions(+), 27 deletions(-) diff --git a/MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift b/MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift index 74145e43..2b343a23 100644 --- a/MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift +++ b/MLS/Data/Data/Network/DTO/AlarmDTO/AlarmResponseDTO.swift @@ -30,7 +30,7 @@ public struct AlarmResponseDTO: Decodable { ) } } - + public struct NormalContent: Decodable { public let type: String public let title: String diff --git a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift index 59f65278..5d2a22b2 100644 --- a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift @@ -58,7 +58,7 @@ public enum CollectionEndPoint { method: .DELETE ) } - + public static func addCollectionAndBookmark(body: Encodable) -> EndPoint { .init( baseURL: base, diff --git a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift index 94b24adf..c1bb3d5c 100644 --- a/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift +++ b/MLS/Data/Data/Repository/CollectionAPIRepositoryImpl.swift @@ -57,12 +57,12 @@ public class CollectionAPIRepositoryImpl: CollectionAPIRepository { let endPoint = CollectionEndPoint.setCollectionName(id: collectionId, body: SetCollectionRequestBody(name: name)) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) } - + public func deleteCollection(collectionId: Int) -> Completable { let endPoint = CollectionEndPoint.deleteCollection(id: collectionId) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) } - + public func addCollectionAndBookmark(collectionIds: [Int], bookmarkIds: [Int]) -> Completable { let endPoint = CollectionEndPoint.addCollectionAndBookmark(body: AddCollectionAndBookmarkBody(collectionIds: collectionIds, bookmarkIds: bookmarkIds)) return provider.requestData(endPoint: endPoint, interceptor: tokenInterceptor) @@ -73,7 +73,7 @@ private extension CollectionAPIRepositoryImpl { struct FetchCollectionListQuery: Encodable { let sort: String? } - + struct CreateCollectionRequestDTO: Encodable { let name: String } @@ -89,7 +89,7 @@ private extension CollectionAPIRepositoryImpl { struct SetCollectionRequestBody: Encodable { let name: String } - + struct AddCollectionAndBookmarkBody: Encodable { let collectionIds: [Int] let bookmarkIds: [Int] diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift index 9a3ecb18..ea42c345 100644 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift @@ -1,8 +1,8 @@ -//import DomainInterface +// import DomainInterface // -//import RxSwift +// import RxSwift // -//public final class AddBookmarksToCollectionUseCaseImpl: AddBookmarksToCollectionUseCase { +// public final class AddBookmarksToCollectionUseCaseImpl: AddBookmarksToCollectionUseCase { // private let repository: CollectionAPIRepository // // public init(repository: CollectionAPIRepository) { @@ -12,4 +12,4 @@ // public func execute(collectionId: Int, bookmarkIds: [Int]) -> Completable { // return repository.addBookmarksToCollection(collectionId: collectionId, bookmarkIds: bookmarkIds) // } -//} +// } diff --git a/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift index 3e1eda24..f1ea702a 100644 --- a/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift @@ -1,7 +1,7 @@ -//import DomainInterface -//import RxSwift +// import DomainInterface +// import RxSwift // -//public final class AddCollectionsToBookmarkUseCaseImpl: AddCollectionsToBookmarkUseCase { +// public final class AddCollectionsToBookmarkUseCaseImpl: AddCollectionsToBookmarkUseCase { // private let repository: CollectionAPIRepository // // public init(repository: CollectionAPIRepository) { @@ -11,4 +11,4 @@ // public func execute(bookmarkId: Int, collectionIds: [Int]) -> Completable { // return repository.addCollectionsToBookmark(bookmarkId: bookmarkId, collectionIds: collectionIds) // } -//} +// } diff --git a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift index bf23db9b..ecdf7645 100644 --- a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift +++ b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift @@ -17,6 +17,6 @@ public protocol CollectionAPIRepository { func setCollectionName(collectionId: Int, name: String) -> Completable func deleteCollection(collectionId: Int) -> Completable - + func addCollectionAndBookmark(collectionIds: [Int], bookmarkIds: [Int]) -> Completable } diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift index ff3d6d64..50cc8f34 100644 --- a/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift +++ b/MLS/Domain/DomainInterface/UseCase/Collection/AddBookmarksToCollectionUseCase.swift @@ -1,5 +1,5 @@ -//import RxSwift +// import RxSwift // -//public protocol AddBookmarksToCollectionUseCase { +// public protocol AddBookmarksToCollectionUseCase { // func execute(collectionId: Int, bookmarkIds: [Int]) -> Completable -//} +// } diff --git a/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift b/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift index ce668445..0fe0a91d 100644 --- a/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift +++ b/MLS/Domain/DomainInterface/UseCase/Collection/AddCollectionsToBookmarkUseCase.swift @@ -1,5 +1,5 @@ -//import RxSwift +// import RxSwift // -//public protocol AddCollectionsToBookmarkUseCase { +// public protocol AddCollectionsToBookmarkUseCase { // func execute(bookmarkId: Int, collectionIds: [Int]) -> Completable -//} +// } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift index e15a72f7..d261c38b 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift @@ -5,7 +5,7 @@ import DomainInterface public final class AddCollectionFactoryImpl: AddCollectionFactory { private let createCollectionListUseCase: CreateCollectionListUseCase private let setCollectionUseCase: SetCollectionUseCase - + public init(createCollectionListUseCase: CreateCollectionListUseCase, setCollectionUseCase: SetCollectionUseCase) { self.createCollectionListUseCase = createCollectionListUseCase self.setCollectionUseCase = setCollectionUseCase diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift index ce534226..91f3f2e3 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionViewController.swift @@ -224,7 +224,7 @@ private extension AddCollectionViewController { self.dismissed.onNext((text)) self.dismissed.onCompleted() } - + self.dismiss(animated: false, completion: completion) }) } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift index bec7fabe..655275dc 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailView.swift @@ -94,7 +94,7 @@ extension CollectionDetailView { listCollectionView.isHidden = isEmpty emptyContainerView.isHidden = !isEmpty } - + func setName(name: String) { navigation.setTitle(title: name) } diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift index e0f69150..d621baf3 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift @@ -38,7 +38,6 @@ public final class CollectionListReactor: Reactor { private let disposeBag = DisposeBag() private let fetchCollectionListUseCase: FetchCollectionListUseCase - public init(fetchCollectionListUseCase: FetchCollectionListUseCase) { self.fetchCollectionListUseCase = fetchCollectionListUseCase diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift index 3881cc33..48c57b5f 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListView.swift @@ -122,7 +122,7 @@ extension CollectionListView { sortButton.setAttributedTitle(.makeStyledString(font: .b_s_r, text: selectedType.rawValue, color: .primary700), for: .normal) sortButton.tintColor = .primary700 } - + func selectSort(selectedType: SortType) { sortButton.setAttributedTitle(.makeStyledString(font: .b_s_r, text: selectedType.rawValue, color: .primary700), for: .normal) sortButton.tintColor = .primary700 diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift index 70520965..b4ea36e7 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift @@ -101,7 +101,7 @@ extension CollectionListViewController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.sortButton.rx.tap .map { .sortButtonTapped } .bind(to: reactor.action) From 5999c7e923efcfe49742f075fad506cf8a2be9c8 Mon Sep 17 00:00:00 2001 From: p2glet Date: Thu, 27 Nov 2025 17:26:52 +0900 Subject: [PATCH 09/10] =?UTF-8?q?fix/#269:=20DomainHTTPError=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DomainInterface/Error/DomainHTTPError.swift.swift | 9 --------- .../BookmarkModal/BookmarkModalReactor.swift | 5 ----- 2 files changed, 14 deletions(-) delete mode 100644 MLS/Domain/DomainInterface/Error/DomainHTTPError.swift.swift diff --git a/MLS/Domain/DomainInterface/Error/DomainHTTPError.swift.swift b/MLS/Domain/DomainInterface/Error/DomainHTTPError.swift.swift deleted file mode 100644 index 209c66ad..00000000 --- a/MLS/Domain/DomainInterface/Error/DomainHTTPError.swift.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -public enum DomainHTTPError: Error, Equatable { - case httpStatus(code: Int, message: String?) - case network - case decode - case noData - case unknown -} diff --git a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift index 1f50c211..121ff92b 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift @@ -59,11 +59,6 @@ public final class BookmarkModalReactor: Reactor { collectionIds: currentState.selectedItems.map { $0.collectionId }, bookmarkIds: currentState.bookmarkIds ) - .do(onError: { error in - if let error = error as? DomainHTTPError { - print(error) - } - }) .andThen(.just(.toNavigate(.dismissWithData))) case .backButtonTapped: From 34dd40de69ee2c4a200dab87065b787c1c48b153 Mon Sep 17 00:00:00 2001 From: p2glet Date: Thu, 27 Nov 2025 17:29:10 +0900 Subject: [PATCH 10/10] =?UTF-8?q?fix/#269:=20guard=EB=AC=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ItemFilterBottomSheetViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift index b3eeb999..ecea331d 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/ItemFilterBottomSheet/ItemFilterBottomSheetViewController.swift @@ -467,8 +467,8 @@ extension ItemFilterBottomSheetViewController { .skip(1) .withUnretained(self) .subscribe { owner, scrolls in - guard owner.dataSource != nil else { return } - var snapshot = owner.dataSource.snapshot() + guard let dataSource = owner.dataSource != nil else { return } + var snapshot = dataSource.snapshot() snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .scrollCategories)) snapshot.appendItems(scrolls.scrollTypes.map { .scrollCategories($0) }, toSection: .scrollCategories) snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .weaponScrolls))