diff --git a/Projects/Features/SearchFeature/Sources/After/Reactors/ListSearchResultReactor.swift b/Projects/Features/SearchFeature/Sources/After/Reactors/ListSearchResultReactor.swift index 73a80af34..4778fb464 100644 --- a/Projects/Features/SearchFeature/Sources/After/Reactors/ListSearchResultReactor.swift +++ b/Projects/Features/SearchFeature/Sources/After/Reactors/ListSearchResultReactor.swift @@ -81,17 +81,16 @@ extension ListSearchResultReactor { private func updateSortType(_ type: SortType) -> Observable { return .concat([ .just(.updateSortType(type)), - updateDataSource(order: type, text: self.text, scrollPage: 1, byOption: true) + updateDataSource(order: type, text: self.text, scrollPage: 1) ]) } private func updateDataSource( order: SortType, text: String, - scrollPage: Int, - byOption: Bool = false // 필터또는 옵션으로 리프래쉬 하나 , 아니면 스크롤이냐 + scrollPage: Int ) -> Observable { - let prev: [SearchPlaylistEntity] = byOption ? [] : self.currentState.dataSource + let prev: [SearchPlaylistEntity] = scrollPage == 1 ? [] : self.currentState.dataSource return .concat([ .just(Mutation.updateLoadingState(true)), diff --git a/Projects/Features/SearchFeature/Sources/After/Reactors/SongSearchResultReactor.swift b/Projects/Features/SearchFeature/Sources/After/Reactors/SongSearchResultReactor.swift index f74b15cae..1c30f0e62 100644 --- a/Projects/Features/SearchFeature/Sources/After/Reactors/SongSearchResultReactor.swift +++ b/Projects/Features/SearchFeature/Sources/After/Reactors/SongSearchResultReactor.swift @@ -1,6 +1,8 @@ import Localization import LogManager import ReactorKit +import RxCocoa +import RxSwift import SearchDomainInterface import SongsDomainInterface @@ -41,6 +43,8 @@ final class SongSearchResultReactor: Reactor { private let fetchSearchSongsUseCase: any FetchSearchSongsUseCase private let text: String private let limit: Int = 20 + private var requestDisposeBag = DisposeBag() + private let subject = PublishSubject() init(text: String, fetchSearchSongsUseCase: any FetchSearchSongsUseCase) { self.initialState = State( @@ -112,6 +116,20 @@ final class SongSearchResultReactor: Reactor { return newState } + + func transform(mutation: Observable) -> Observable { + let flatMapMutation = subject + .withUnretained(self) + .flatMap { owner, subjectMutation -> Observable in + return .concat([ + .just(subjectMutation), + .just(Mutation.updateScrollPage(owner.currentState.scrollPage + 1)), + .just(Mutation.updateLoadingState(false)) + ]) + } + + return Observable.merge(mutation, flatMapMutation) + } } extension SongSearchResultReactor { @@ -121,7 +139,7 @@ extension SongSearchResultReactor { return .concat([ .just(.updateSelectedCount(0)), .just(.updateSortType(type)), - updateDataSource(order: type, filter: state.filterType, text: self.text, scrollPage: 1, byOption: true) + updateDataSource(order: type, filter: state.filterType, text: self.text, scrollPage: 1) ]) } @@ -131,7 +149,7 @@ extension SongSearchResultReactor { return .concat([ .just(.updateSelectedCount(0)), .just(.updateFilterType(type)), - updateDataSource(order: state.sortType, filter: type, text: self.text, scrollPage: 1, byOption: true) + updateDataSource(order: state.sortType, filter: type, text: self.text, scrollPage: 1) ]) } @@ -139,39 +157,40 @@ extension SongSearchResultReactor { order: SortType, filter: FilterType, text: String, - scrollPage: Int, - byOption: Bool = false // 필터또는 옵션으로 리프래쉬 하나 , 아니면 스크롤이냐 + scrollPage: Int ) -> Observable { - return .concat([ - .just(.updateLoadingState(true)), - fetchSearchSongsUseCase - .execute(order: order, filter: filter, text: text, page: scrollPage, limit: limit) - .asObservable() - .map { [weak self] dataSource -> Mutation in - - guard let self else { return .updateDataSource(dataSource: [], canLoad: false) } - - let prev: [SongEntity] = byOption ? [] : self.currentState.dataSource - - if scrollPage == 1 { - LogManager.analytics(SearchAnalyticsLog.viewSearchResult( - keyword: self.text, - category: "song", - count: dataSource.count - )) - } - - return Mutation.updateDataSource(dataSource: prev + dataSource, canLoad: dataSource.count == limit) + requestDisposeBag = DisposeBag() // 기존 작업 캔슬 + + fetchSearchSongsUseCase + .execute(order: order, filter: filter, text: text, page: scrollPage, limit: limit) + .asObservable() + .map { [weak self] dataSource -> Mutation in + + guard let self else { return .updateDataSource(dataSource: [], canLoad: false) } + + let prev: [SongEntity] = scrollPage == 1 ? [] : self.currentState.dataSource + if scrollPage == 1 { + LogManager.analytics(SearchAnalyticsLog.viewSearchResult( + keyword: self.text, + category: "song", + count: dataSource.count + )) } - .catch { error in - let wmErorr = error.asWMError - return Observable.just( - Mutation.showToast(wmErorr.errorDescription ?? LocalizationStrings.unknownErrorWarning) - ) - }, - .just(Mutation.updateScrollPage(scrollPage + 1)), // 스크롤 페이지 증가 - .just(.updateLoadingState(false)) - ]) + + return Mutation.updateDataSource(dataSource: prev + dataSource, canLoad: dataSource.count == limit) + } + .catch { error in + let wmErorr = error.asWMError + return Observable.just( + Mutation.showToast(wmErorr.errorDescription ?? LocalizationStrings.unknownErrorWarning) + ) + } + .bind(with: subject, onNext: { subject, mutation in + subject.onNext(mutation) + }) + .disposed(by: requestDisposeBag) + + return Observable.just(.updateLoadingState(true)) } func updateItemSelected(_ index: Int) -> Observable { diff --git a/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift b/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift index 91f38fea0..f88933871 100644 --- a/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift +++ b/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift @@ -24,7 +24,7 @@ final class ListSearchResultViewController: BaseReactorViewController() + sharedState.map(\.dataSource) + .bind(with: self) { owner, dataSource in - snapshot.appendSections([.list]) + var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendItems(dataSource, toSection: .list) - owner.dataSource.apply(snapshot, animatingDifferences: false) + snapshot.appendSections([.list]) - let warningView = WMWarningView( - text: "검색결과가 없습니다." - ) + snapshot.appendItems(dataSource, toSection: .list) + owner.dataSource.apply(snapshot, animatingDifferences: false) - if dataSource.isEmpty { - owner.collectionView.setBackgroundView(warningView, 100) - } else { - owner.collectionView.restore() - } + let warningView = WMWarningView( + text: "검색결과가 없습니다." + ) + + if dataSource.isEmpty { + owner.collectionView.setBackgroundView(warningView, 100) + } else { + owner.collectionView.restore() } } .disposed(by: disposeBag) diff --git a/Projects/Features/SearchFeature/Sources/After/ViewControllers/SongSearchResultViewController.swift b/Projects/Features/SearchFeature/Sources/After/ViewControllers/SongSearchResultViewController.swift index dc410f8c8..3a4585084 100644 --- a/Projects/Features/SearchFeature/Sources/After/ViewControllers/SongSearchResultViewController.swift +++ b/Projects/Features/SearchFeature/Sources/After/ViewControllers/SongSearchResultViewController.swift @@ -170,8 +170,8 @@ final class SongSearchResultViewController: BaseReactorViewController() snapshot.appendSections([.song]) diff --git a/Projects/Features/SearchFeature/Sources/Before/ViewControllers/BeforeSearchContentViewController.swift b/Projects/Features/SearchFeature/Sources/Before/ViewControllers/BeforeSearchContentViewController.swift index af3e306ff..9302e93f2 100644 --- a/Projects/Features/SearchFeature/Sources/Before/ViewControllers/BeforeSearchContentViewController.swift +++ b/Projects/Features/SearchFeature/Sources/Before/ViewControllers/BeforeSearchContentViewController.swift @@ -352,6 +352,6 @@ extension BeforeSearchContentViewController: BeforeSearchSectionHeaderViewDelega extension BeforeSearchContentViewController { func scrollToTop() { - tableView.setContentOffset(.zero, animated: true) + collectionView.scroll(to: .top) } }