diff --git a/MLS/.swiftlint.yml b/MLS/.swiftlint.yml index 1ae09fc6..6fa49957 100644 --- a/MLS/.swiftlint.yml +++ b/MLS/.swiftlint.yml @@ -13,3 +13,15 @@ function_body_length: disabled_rules: - force_cast - blanket_disable_command + +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..2b343a23 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 1f4e98c2..5d2a22b2 100644 --- a/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift +++ b/MLS/Data/Data/Network/Endpoints/CollectionEndPoint.swift @@ -3,14 +3,19 @@ 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 { .init(baseURL: base, path: "/api/v1/collections", method: .POST, body: body) } - + public static func fetchCollection(id: Int) -> ResponsableEndPoint<[BookmarkDTO]> { .init( baseURL: base, @@ -18,4 +23,48 @@ public enum CollectionEndPoint { method: .GET ) } + + public static func addBookmarksToCollection(id: Int, body: Encodable) -> EndPoint { + .init( + baseURL: base, + path: "/api/v1/collections/\(id)/bookmarks", + method: .POST, + body: body + ) + } + + public static func addCollectionsToBookmark(id: Int, body: Encodable) -> EndPoint { + .init( + baseURL: base, + path: "/api/v1/bookmarks/\(id)/collections", + method: .POST, + body: body + ) + } + + public static func setCollectionName(id: Int, body: Encodable) -> EndPoint { + .init( + baseURL: base, + path: "/api/v1/collections/\(id)", + method: .PUT, + body: body + ) + } + + public static func deleteCollection(id: Int) -> EndPoint { + .init( + baseURL: base, + path: "/api/v1/collections/\(id)", + 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 e6831513..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 ) } @@ -34,11 +34,20 @@ 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: ",") - 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 4332421c..c1bb3d5c 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() } } @@ -30,10 +30,68 @@ 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) +// .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) + } + + 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) + } } private extension CollectionAPIRepositoryImpl { + struct FetchCollectionListQuery: Encodable { + let sort: String? + } + struct CreateCollectionRequestDTO: Encodable { let name: String } + + struct AddBookmarkRequestBody: Encodable { + let bookmarkIds: [Int] + } + + struct AddCollectionRequestBody: Encodable { + let collectionIds: [Int] + } + + struct SetCollectionRequestBody: Encodable { + let name: String + } + + struct AddCollectionAndBookmarkBody: Encodable { + let collectionIds: [Int] + let bookmarkIds: [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/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/Collection/AddBookmarksToCollectionUseCaseImpl.swift new file mode 100644 index 00000000..ea42c345 --- /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/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 new file mode 100644 index 00000000..f1ea702a --- /dev/null +++ b/MLS/Domain/Domain/UseCaseImpl/Collection/AddCollectionsToBookmarkUseCaseImpl.swift @@ -0,0 +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) +// } +// } 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/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/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/Repository/CollectionAPIRepository.swift b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift index 8c80c87c..ecdf7645 100644 --- a/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift +++ b/MLS/Domain/DomainInterface/Repository/CollectionAPIRepository.swift @@ -4,9 +4,19 @@ import RxSwift public protocol CollectionAPIRepository { // 컬렉션 목록 조회 - func fetchCollectionList() -> Observable<[CollectionResponse]> + func fetchCollectionList(sort: String?) -> Observable<[CollectionResponse]> // 컬렉션 목록 추가 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 + + 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 new file mode 100644 index 00000000..50cc8f34 --- /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/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 new file mode 100644 index 00000000..0fe0a91d --- /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/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/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/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/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 065d02a5..3312149e 100644 --- a/MLS/MLS/Application/AppDelegate.swift +++ b/MLS/MLS/Application/AppDelegate.swift @@ -1,5 +1,8 @@ // swiftlint:disable function_body_length -// swiftlint:disable line_length + +import os +import UIKit +import UserNotifications import AuthFeature import AuthFeatureInterface @@ -14,13 +17,11 @@ import DictionaryFeature import DictionaryFeatureInterface import Domain import DomainInterface -import Firebase -import KakaoSDKCommon import MyPageFeature import MyPageFeatureInterface -import UIKit -import UserNotifications -import os + +import Firebase +import KakaoSDKCommon @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -327,8 +328,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)) @@ -510,6 +510,21 @@ 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: 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: AddCollectionAndBookmarkUseCase.self) { + AddCollectionAndBookmarkUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + } } fileprivate func registerFactory() { @@ -523,12 +538,14 @@ 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( addCollectionFactory: DIContainer.resolve( - type: AddCollectionFactory.self)) + type: AddCollectionFactory.self), fetchCollectionListUseCase: DIContainer.resolve(type: FetchCollectionListUseCase.self), + addCollectionAndBookmarkUseCase: DIContainer.resolve(type: AddCollectionAndBookmarkUseCase.self) + ) } DIContainer.register(type: LoginFactory.self) { LoginFactoryImpl( @@ -769,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( @@ -785,19 +804,18 @@ 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( - collectionListUseCase: DIContainer.resolve( + fetchCollectionListUseCase: DIContainer.resolve( type: FetchCollectionListUseCase.self), - createCollectionListUseCase: DIContainer.resolve( - type: CreateCollectionListUseCase.self), 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( @@ -820,7 +838,10 @@ extension AppDelegate { DIContainer .resolve(type: SetBookmarkUseCase.self), fetchCollectionUseCase: DIContainer.resolve( - type: FetchCollectionUseCase.self) + type: FetchCollectionUseCase.self), + deleteCollectionUseCase: DIContainer + .resolve(type: DeleteCollectionUseCase.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/AddCollection/AddCollectionFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/AddCollection/AddCollectionFactoryImpl.swift index 1480acd1..d261c38b 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 func make(collection: CollectionResponse?, onDismissWithMessage: @escaping (CollectionResponse?) -> Void) -> BaseViewController { + public init(createCollectionListUseCase: CreateCollectionListUseCase, setCollectionUseCase: SetCollectionUseCase) { + self.createCollectionListUseCase = createCollectionListUseCase + self.setCollectionUseCase = setCollectionUseCase + } + + 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..91f3f2e3 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/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 ced8efbc..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) @@ -170,18 +177,18 @@ 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 { 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 @@ -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 c03c2981..669604f3 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalFactoryImpl.swift @@ -4,17 +4,27 @@ import DomainInterface public final class BookmarkModalFactoryImpl: BookmarkModalFactory { private let addCollectionFactory: AddCollectionFactory + private let fetchCollectionListUseCase: FetchCollectionListUseCase + private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase - public init(addCollectionFactory: AddCollectionFactory) { + public init(addCollectionFactory: AddCollectionFactory, fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase) { self.addCollectionFactory = addCollectionFactory + self.fetchCollectionListUseCase = fetchCollectionListUseCase + self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase } - public func make(onDismissWithColletions: (([CollectionResponse?]) -> Void)?, onDismissWithMessage: ((CollectionResponse?) -> Void)?) -> BaseViewController { - let reactor = BookmarkModalReactor() + public func make(bookmarkIds: [Int]) -> BaseViewController { + let reactor = BookmarkModalReactor(bookmarkIds: bookmarkIds, fetchCollectionListUseCase: fetchCollectionListUseCase, addCollectionAndBookmarkUseCase: addCollectionAndBookmarkUseCase) let viewController = BookmarkModalViewController(addCollectionFactory: addCollectionFactory) viewController.reactor = reactor - viewController.onDismissWithMessage = onDismissWithMessage - viewController.onDismissWithCollections = onDismissWithColletions + return viewController + } + + public func make(bookmarkIds: [Int], onComplete: ((Bool) -> Void)? = nil) -> BaseViewController { + let viewController = make(bookmarkIds: bookmarkIds) + 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 9a39d87c..121ff92b 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/BookmarkModal/BookmarkModalReactor.swift @@ -2,27 +2,34 @@ 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 } public enum Mutation { case toNavigate(Route) case checkCollection([CollectionResponse]) + case setCollection([CollectionResponse]) } public struct State { @Pulse var route: Route + var bookmarkIds: [Int] var collections = [CollectionResponse]() var selectedItems = [CollectionResponse]() } @@ -31,16 +38,35 @@ public final class BookmarkModalReactor: Reactor { private let disposeBag = DisposeBag() - public init() { - self.initialState = State(route: .none) + private let fetchCollectionListUseCase: FetchCollectionListUseCase + private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase + + public init(bookmarkIds: [Int], fetchCollectionListUseCase: FetchCollectionListUseCase, addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase) { + self.initialState = State(route: .none, bookmarkIds: bookmarkIds) + self.fetchCollectionListUseCase = fetchCollectionListUseCase + self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase } public func mutate(action: Action) -> Observable { switch action { + case .viewWillAppear, .completeAdding: + return fetchCollectionListUseCase.execute(sort: nil) + .map { .setCollection($0) } + + case .addButtonTapped: + return addCollectionAndBookmarkUseCase + .execute( + collectionIds: currentState.selectedItems.map { $0.collectionId }, + bookmarkIds: currentState.bookmarkIds + ) + .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 }) { @@ -59,6 +85,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..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 @@ -80,10 +78,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,24 +99,44 @@ 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) 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/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/CollectionDetailFactoryImpl.swift b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift index 4d9c1ef2..83341f3f 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionDetail/CollectionDetailFactoryImpl.swift @@ -1,17 +1,22 @@ 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 private let addCollectionFactory: AddCollectionFactory private let collectionEditFactory: CollectionEditFactory private let dictionaryDetailFactory: DictionaryDetailFactory - + private let setBookmarkUseCase: SetBookmarkUseCase private let fetchCollectionUseCase: FetchCollectionUseCase + private let deleteCollectionUseCase: DeleteCollectionUseCase + private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase public init( bookmarkModalFactory: BookmarkModalFactory, @@ -20,7 +25,9 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { collectionEditFactory: CollectionEditFactory, dictionaryDetailFactory: DictionaryDetailFactory, setBookmarkUseCase: SetBookmarkUseCase, - fetchCollectionUseCase: FetchCollectionUseCase + fetchCollectionUseCase: FetchCollectionUseCase, + deleteCollectionUseCase: DeleteCollectionUseCase, + addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase ) { self.bookmarkModalFactory = bookmarkModalFactory self.collectionSettingFactory = collectionSettingFactory @@ -29,14 +36,19 @@ public final class CollectionDetailFactoryImpl: CollectionDetailFactory { self.dictionaryDetailFactory = dictionaryDetailFactory self.setBookmarkUseCase = setBookmarkUseCase self.fetchCollectionUseCase = fetchCollectionUseCase + self.deleteCollectionUseCase = deleteCollectionUseCase + self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase } - public func make(collection: CollectionResponse) -> BaseViewController { + public func make(collection: CollectionResponse, onMoveToMain: (() -> Void)?) -> BaseViewController { let reactor = CollectionDetailReactor( + collection: collection, setBookmarkUseCase: setBookmarkUseCase, fetchCollectionUseCase: fetchCollectionUseCase, - collection: collection + deleteCollectionUseCase: deleteCollectionUseCase, + addCollectionAndBookmarkUseCase: addCollectionAndBookmarkUseCase ) + let viewController = CollectionDetailViewController( reactor: reactor, bookmarkModalFactory: bookmarkModalFactory, @@ -45,6 +57,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..1df67134 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 { @@ -41,28 +42,31 @@ public final class CollectionDetailReactor: Reactor { } let type = DictionaryMainViewType.bookmark var collection: CollectionResponse - var bookmarks = [BookmarkResponse]() var lastDeletedBookmark: BookmarkResponse? } // MARK: - Properties private let setBookmarkUseCase: SetBookmarkUseCase private let fetchCollectionUseCase: FetchCollectionUseCase + private let deleteCollectionUseCase: DeleteCollectionUseCase + private let addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase 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, addCollectionAndBookmarkUseCase: AddCollectionAndBookmarkUseCase) { self.initialState = State(route: .none, collection: collection) self.setBookmarkUseCase = setBookmarkUseCase self.fetchCollectionUseCase = fetchCollectionUseCase + self.deleteCollectionUseCase = deleteCollectionUseCase + self.addCollectionAndBookmarkUseCase = addCollectionAndBookmarkUseCase } 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 +88,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): @@ -93,21 +96,28 @@ 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(addCollectionAndBookmarkUseCase.execute(collectionIds: [currentState.collection.collectionId], bookmarkIds: [lastDeletedBookmarkId])) .andThen( Observable.concat([ - // 불러오기 + fetchCollectionUseCase.execute(id: currentState.collection.collectionId) + .map { .setItems($0) }, .just(.setLastDeletedBookmark(nil)) ]) ) 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))) + case .deleteCollection: + return deleteCollectionUseCase.execute(collectionId: currentState.collection.collectionId) + .andThen(.just(.navigateTo(.dismiss))) } } @@ -115,7 +125,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..655275dc 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() } } @@ -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 1b740217..c30e3e90 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 @@ -121,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) @@ -134,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( @@ -148,7 +172,7 @@ extension CollectionDetailViewController { ctaText: "삭제하기", cancelText: "취소", ctaAction: { - // 삭제 처리 + reactor.action.onNext(.deleteCollection) }, cancelAction: {} ) @@ -162,10 +186,9 @@ extension CollectionDetailViewController { .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } .withUnretained(self) + .observe(on: MainScheduler.instance) .subscribe { owner, route in switch route { - case .toMain: - owner.tabBarController?.selectedIndex = 0 case .dismiss: owner.navigationController?.popViewController(animated: true) case .edit: @@ -200,58 +223,35 @@ extension CollectionDetailViewController: UICollectionViewDelegate, UICollection else { return UICollectionViewCell() } + 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 } -// 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) -// } -// }) -// } -// } -// } -// ) + 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/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 f11a1710..2a0627c7 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,16 +121,13 @@ 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: - let viewController = owner.bookmarkModalFactory.make(onDismissWithColletions: { _ in - - }, onDismissWithMessage: { _ in - - }) + let viewController = owner.bookmarkModalFactory.make(bookmarkIds: reactor.currentState.selectedItems.map { $0.bookmarkId }) owner.present(viewController, animated: true) default: break @@ -141,7 +140,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 { @@ -150,25 +149,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..6d0d0513 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListFactoryImpl.swift @@ -1,23 +1,24 @@ import BaseFeature import BookmarkFeatureInterface +import DictionaryFeatureInterface 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 + private let sortedBottomSheetFactory: SortedBottomSheetFactory - public init(collectionListUseCase: FetchCollectionListUseCase, createCollectionListUseCase: CreateCollectionListUseCase, addCollectionFactory: AddCollectionFactory, bookmarkDetailFactory: CollectionDetailFactory) { - self.collectionListUseCase = collectionListUseCase - self.createCollectionListUseCase = createCollectionListUseCase + 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(collectionListUseCase: collectionListUseCase, createCollectionListUseCase: createCollectionListUseCase) - let viewController = CollectionListViewController(addCollectionFactory: addCollectionFactory, detailFactory: bookmarkDetailFactory) + let reactor = CollectionListReactor(fetchCollectionListUseCase: fetchCollectionListUseCase) + 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 f0443c02..d621baf3 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListReactor.swift @@ -9,50 +9,57 @@ public final class CollectionListReactor: Reactor { public enum Route { case none case detail(CollectionResponse) + case sortFilter } public enum Action { case itemTapped(Int) case viewWillAppear - case addCollection(String) + 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 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(sort: currentState.selectedSort).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(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) } + ]) } } @@ -63,6 +70,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..48c57b5f 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 7269b4f2..b4ea36e7 100644 --- a/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift +++ b/MLS/Presentation/BookmarkFeature/BookmarkFeature/CollectionList/CollectionListViewController.swift @@ -2,6 +2,8 @@ import UIKit import BaseFeature import BookmarkFeatureInterface +import DesignSystem +import DictionaryFeatureInterface import DomainInterface import ReactorKit @@ -12,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() } @@ -61,14 +66,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) } } @@ -94,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) { @@ -101,11 +113,24 @@ 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): - 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) + 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 } 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 acea1e83..8c96281c 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(onDismissWithColletions: (([CollectionResponse?]) -> Void)?, onDismissWithMessage: ((CollectionResponse?) -> Void)?) -> BaseViewController + func make(bookmarkIds: [Int]) -> BaseViewController + func make(bookmarkIds: [Int], onComplete: ((Bool) -> 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/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/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 4e7fd8f4..7b65a1e8 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 { @@ -254,13 +255,14 @@ 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: "북마크를 하려면 로그인이 필요해요.", @@ -294,24 +296,26 @@ 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 + guard let self, + let id = bookmarkId else { return } + let viewController = self.bookmarkModalFactory.make(bookmarkIds: [id], onComplete: { isAdd in + if isAdd { + DispatchQueue.main.async { ToastFactory.createToast( - message: "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." + message: + "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." ) } - ) - viewController.modalPresentationStyle = .pageSheet - if let sheet = viewController.sheetPresentationController { - sheet.detents = [.medium(), .large()] - sheet.prefersGrabberVisible = true - sheet.preferredCornerRadius = 16 } - 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/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/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/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/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/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..621a2c83 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 @@ -52,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 @@ -154,7 +150,7 @@ open class DictionaryListReactor: Reactor { } } - // MARK: - Fetch (완전 통합) + // MARK: - Fetch private func fetchList( sort: String?, startLevel: Int?, @@ -181,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 069ac574..c1700ee7 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() @@ -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,30 +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? { @@ -251,7 +235,8 @@ extension DictionaryListViewController: UICollectionViewDelegate, let viewController = self.loginFactory.make( exitRoute: .pop) self.navigationController?.pushViewController( - viewController, animated: true) + viewController, animated: true + ) }, cancelAction: nil ) @@ -282,21 +267,22 @@ extension DictionaryListViewController: UICollectionViewDelegate, 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 { + 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(bookmarkIds: [id], onComplete: { isAdd in + if isAdd { + ToastFactory.createToast( + message: + "컬렉션에 추가되었어요. 북마크 탭에서 확인 할 수 있어요." + ) + } + }) + + viewController.modalPresentationStyle = .pageSheet + if let sheet = viewController.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersGrabberVisible = true sheet.preferredCornerRadius = 16 @@ -328,7 +314,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 +326,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) // 해당 페이지로 데이터 불러오기 } } } 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..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 let dataSource = owner.dataSource 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)) 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