From 6ee1f8f512a2035f5b222d1c38e227f0143afcd1 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 22:54:30 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[IDLE-000]=20=EC=9A=94=EC=96=91=EB=B3=B4?= =?UTF-8?q?=ED=98=B8=EC=82=AC=EA=B0=80=20=ED=99=95=EC=9D=B8=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8A=94=20=EA=B3=B5=EA=B3=A0=EA=B0=80=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=ED=91=9C=EC=B6=9C?= =?UTF-8?q?=EB=90=A0=20=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WorkerBoardEmptyView.swift | 71 +++++++++++++++++++ .../WorkerRecruitmentPostBoardVC.swift | 25 +++++-- 2 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerBoardEmptyView.swift diff --git a/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerBoardEmptyView.swift b/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerBoardEmptyView.swift new file mode 100644 index 00000000..8588e1f5 --- /dev/null +++ b/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerBoardEmptyView.swift @@ -0,0 +1,71 @@ +// +// WorkerBoardEmptyView.swift +// WorkerFeature +// +// Created by choijunios on 9/12/24. +// + +import UIKit +import DSKit + +import RxSwift +import RxCocoa + +class WorkerBoardEmptyView: UIView { + + let titleLabel: IdleLabel = { + let label = IdleLabel(typography: .Heading2) + label.textString = "아직 해당 지역의 공고가 없어요." + label.textAlignment = .center + return label + }() + + let descriptionLabel: IdleLabel = { + let label = IdleLabel(typography: .Body3) + label.attrTextColor = DSColor.gray300.color + label.numberOfLines = 3 + label.textString = "나의 위치를 근처 다른 지역으로 바꿔\n새로운 공고를 탐색해보세요.\n나의 위치는 추후에 다시 변경할 수 있어요." + label.textAlignment = .center + return label + }() + + let editProfile: IdleThirdinaryButton = { + let button = IdleThirdinaryButton(level: .medium) + button.label.textString = "내 프로필 수정" + return button + }() + + init() { + super.init(frame: .zero) + + setAppearance() + setLayout() + } + required init?(coder: NSCoder) { nil } + + func setAppearance() { + self.backgroundColor = .clear + } + + func setLayout() { + + let mainStack = VStack([ + titleLabel, + Spacer(height: 8), + descriptionLabel, + Spacer(height: 20), + editProfile, + ]) + + self.addSubview(mainStack) + mainStack.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + + editProfile.widthAnchor.constraint(equalToConstant: 165), + + mainStack.centerXAnchor.constraint(equalTo: self.centerXAnchor), + mainStack.centerYAnchor.constraint(equalTo: self.centerYAnchor), + ]) + } +} diff --git a/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift b/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift index ee8ae4e9..8a82f425 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift @@ -25,6 +25,11 @@ public class WorkerRecruitmentPostBoardVC: BaseViewController { }() let postTableView = UITableView() let tableHeader = BoardSortigHeaderView() + let emptyScreen: WorkerBoardEmptyView = { + let view = WorkerBoardEmptyView() + view.isHidden = true + return view + }() // Paging var isPaging = true @@ -61,6 +66,7 @@ public class WorkerRecruitmentPostBoardVC: BaseViewController { .postBoardData? .drive(onNext: { [weak self] (isRefreshed: Bool, postData) in guard let self else { return } + self.postData = postData postTableView.reloadData() isPaging = false @@ -70,6 +76,9 @@ public class WorkerRecruitmentPostBoardVC: BaseViewController { self?.postTableView.setContentOffset(.zero, animated: false) } } + + // 공고가 없을 경우 + emptyScreen.isHidden = (postData.count != 0) }) .disposed(by: disposeBag) @@ -119,7 +128,8 @@ public class WorkerRecruitmentPostBoardVC: BaseViewController { [ topContainer, - postTableView + postTableView, + emptyScreen, ].forEach { $0.translatesAutoresizingMaskIntoConstraints = false view.addSubview($0) @@ -127,13 +137,18 @@ public class WorkerRecruitmentPostBoardVC: BaseViewController { NSLayoutConstraint.activate([ topContainer.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - topContainer.leftAnchor.constraint(equalTo: view.leftAnchor), - topContainer.rightAnchor.constraint(equalTo: view.rightAnchor), + topContainer.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), + topContainer.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), postTableView.topAnchor.constraint(equalTo: topContainer.bottomAnchor), - postTableView.leftAnchor.constraint(equalTo: view.leftAnchor), - postTableView.rightAnchor.constraint(equalTo: view.rightAnchor), + postTableView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), + postTableView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), postTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + + emptyScreen.topAnchor.constraint(equalTo: topContainer.bottomAnchor), + emptyScreen.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), + emptyScreen.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), + emptyScreen.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) } From dca1c1996fbcfcac8c24277a69f201347b01d99c Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 23:08:07 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[IDLE-000]=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=ED=99=94=EB=A9=B4=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AppliedAndLikedBoardCoordinator.swift | 2 ++ .../WorkerRecruitmentBoardCoordinator.swift | 13 +++++++++++++ .../WorkerRecruitmentPostBoardVC.swift | 5 +++++ .../WorkerRecruitmentPostBoardVM.swift | 14 ++++++++++++++ .../WorkerRecruitmentBoardCoordinatable.swift | 3 +++ 5 files changed, 37 insertions(+) diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift index f7fb2469..711e5ecb 100644 --- a/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift @@ -92,4 +92,6 @@ extension AppliedAndLikedBoardCoordinator { coordinator.parent = self coordinator.start() } + + func showWorkerProfile() { } } diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift index b4006799..47605e5d 100644 --- a/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift @@ -81,5 +81,18 @@ extension WorkerRecruitmentBoardCoordinator { coordinator.parent = self coordinator.start() } + + public func showWorkerProfile() { + let coordinator = WorkerProfileCoordinator( + dependency: .init( + profileMode: .myProfile, + navigationController: navigationController, + workerProfileUseCase: injector.resolve(WorkerProfileUseCase.self) + ) + ) + addChildCoordinator(coordinator) + coordinator.parent = self + coordinator.start() + } } diff --git a/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift b/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift index 8a82f425..d67cedb3 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift @@ -90,6 +90,11 @@ public class WorkerRecruitmentPostBoardVC: BaseViewController { .disposed(by: disposeBag) // Input + self.emptyScreen + .editProfile.rx.tap + .bind(to: viewModel.editProfileButtonClicked) + .disposed(by: disposeBag) + self.rx.viewDidLoad .bind(to: viewModel.requestWorkerLocation) .disposed(by: disposeBag) diff --git a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift index c8c74b5c..ea58c765 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift @@ -45,6 +45,9 @@ public protocol WorkerRecruitmentPostBoardVMable: WorkerAppliablePostBoardVMable /// 요양보호사 위치정보를 요청합니다. var requestWorkerLocation: PublishRelay { get } + /// 프로필 수정버튼이 눌린 경우 + var editProfileButtonClicked: PublishRelay { get } + /// 요양보호사 위치 정보를 전달합니다. var workerLocationTitleText: Driver? { get } } @@ -56,7 +59,9 @@ public class WorkerRecruitmentPostBoardVM: BaseViewModel, WorkerRecruitmentPostB public var workerLocationTitleText: Driver? public var idleAlertVM: RxCocoa.Driver? + // Input + public var editProfileButtonClicked: PublishRelay = .init() public var requestInitialPageRequest: PublishRelay = .init() public var requestWorkerLocation: PublishRelay = .init() public var requestNextPage: PublishRelay = .init() @@ -267,6 +272,15 @@ public class WorkerRecruitmentPostBoardVM: BaseViewModel, WorkerRecruitmentPostB ) } + // MARK: 프로필 수정 + editProfileButtonClicked + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] in + guard let self else { return } + self.coordinator?.showWorkerProfile() + }) + .disposed(by: dispostBag) + Observable .merge( applyRequestFailureAlert, diff --git a/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/RecruitmentPost/Worker/WorkerRecruitmentBoardCoordinatable.swift b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/RecruitmentPost/Worker/WorkerRecruitmentBoardCoordinatable.swift index bfd1a779..8fd0e4e1 100644 --- a/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/RecruitmentPost/Worker/WorkerRecruitmentBoardCoordinatable.swift +++ b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/RecruitmentPost/Worker/WorkerRecruitmentBoardCoordinatable.swift @@ -14,4 +14,7 @@ public protocol WorkerRecruitmentBoardCoordinatable: ParentCoordinator { /// 센터 프로필을 표시합니다. func showCenterProfile(centerId: String) + + /// 요양보호사의 프로필을 표시합니다. + func showWorkerProfile() } From e1d27993f3862bdb702c0f5f3fc012ae34715bf9 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 23:19:55 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[IDLE-000]=20=EC=B2=AB=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=8B=9C=20=ED=85=8C=EC=9D=B4=EB=B8=94=EB=B7=B0=EC=97=90=20?= =?UTF-8?q?=EB=B0=98=EA=B3=B5=EA=B0=84=EC=9D=B4=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=9E=90=EB=8F=99=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=8B=A4=EC=9D=8C=EC=9A=94=EC=B2=AD=20=EC=8B=A4=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WorkerRecruitmentPostBoardVC.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift b/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift index d67cedb3..6866201f 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVC.swift @@ -68,6 +68,7 @@ public class WorkerRecruitmentPostBoardVC: BaseViewController { guard let self else { return } self.postData = postData + postTableView.reloadData() isPaging = false @@ -77,6 +78,13 @@ public class WorkerRecruitmentPostBoardVC: BaseViewController { } } + postTableView.layoutIfNeeded() + if self.checkScrollViewHasSpace() { + // 빈공간이 있는 경우 바로 다음요청 + requestNextPage.accept(()) + } + + // 공고가 없을 경우 emptyScreen.isHidden = (postData.count != 0) }) @@ -160,6 +168,10 @@ public class WorkerRecruitmentPostBoardVC: BaseViewController { private func setObservable() { } + + func checkScrollViewHasSpace() -> Bool { + postTableView.contentSize.height < postTableView.frame.height + } } extension WorkerRecruitmentPostBoardVC: UITableViewDataSource, UITableViewDelegate { From 20f501c714bc258a524251a2c7e41c60af202613 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 23:37:23 +0900 Subject: [PATCH 4/7] =?UTF-8?q?[IDLE-000]=20=EA=B3=B5=EA=B3=A0=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=EC=97=90=EC=84=9C=20=EC=A7=80=EC=9B=90=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=EC=8B=9C=20=EC=A7=80=EC=9B=90=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20disabled=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NativePostDetailForWorkerVC.swift | 8 ++-- .../NativePostDetailForWorkerVM.swift | 47 ++++++------------- .../Sources/ViewModelType/BaseViewModel.swift | 1 + .../WorkerRecruitmentPostBoardVM.swift | 26 +--------- 4 files changed, 23 insertions(+), 59 deletions(-) diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift index dfd8de35..c9568c07 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift @@ -203,10 +203,12 @@ public class NativePostDetailForWorkerVC: BaseViewController { }) .disposed(by: disposeBag) + // 지원성공시 비활성화 viewModel - .alertDriver? - .drive(onNext: { [weak self] alertVO in - self?.showAlert(vo: alertVO) + .applySuccess? + .drive(onNext: { [weak self] in + guard let self else { return } + self.applyButton.setEnabled(false) }) .disposed(by: disposeBag) diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/ViewModel/NativePostDetailForWorkerVM.swift b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/ViewModel/NativePostDetailForWorkerVM.swift index 110e354d..978b2b0b 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/ViewModel/NativePostDetailForWorkerVM.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/ViewModel/NativePostDetailForWorkerVM.swift @@ -20,6 +20,7 @@ public protocol NativePostDetailForWorkerViewModelable: BaseViewModel { var locationInfo: Driver? { get } var idleAlertVM: Driver? { get } var starButtonRequestResult: Driver? { get } + var applySuccess: Driver? { get } // Input var viewWillAppear: PublishRelay { get } @@ -44,6 +45,7 @@ public class NativePostDetailForWorkerVM: BaseViewModel ,NativePostDetailForWork public var locationInfo: RxCocoa.Driver? public var idleAlertVM: Driver? public var starButtonRequestResult: Driver? + public var applySuccess: RxCocoa.Driver? // Input public var backButtonClicked: RxRelay.PublishRelay = .init() @@ -67,11 +69,6 @@ public class NativePostDetailForWorkerVM: BaseViewModel ,NativePostDetailForWork super.init() - // MARK: 로딩 옵저버블 - var loadingStartObservables: [Observable] = [] - var loadingEndObservables: [Observable] = [] - - let getPostDetailResult = viewWillAppear .flatMap { [recruitmentPostUseCase] _ in recruitmentPostUseCase @@ -153,25 +150,29 @@ public class NativePostDetailForWorkerVM: BaseViewModel ,NativePostDetailForWork } } .asDriver(onErrorDriveWith: .never()) - - // 로딩 시작 - loadingStartObservables.append(applyRequest.map { _ in }) - let applyRequestResult = applyRequest + let applyRequestResult = mapEndLoading(mapStartLoading(applyRequest.asObservable()) .flatMap { [recruitmentPostUseCase] _ in // 리스트화면에서는 앱내 지원만 지원합니다. - return recruitmentPostUseCase + recruitmentPostUseCase .applyToPost(postId: postId, method: .app) - } + }) .share() - // 로딩 종료 - loadingEndObservables.append(applyRequestResult.map { _ in }) - let applyRequestSuccess = applyRequestResult.compactMap { $0.value } let applyRequestFailure = applyRequestResult.compactMap { $0.error } + self.applySuccess = applyRequestSuccess + .asDriver(onErrorDriveWith: .never()) + + applyRequestSuccess + .subscribe { [weak self] _ in + guard let self else { return } + self.snackBar.onNext(.init(titleText: "지원이 완료되었어요.")) + } + .disposed(by: disposeBag) + let applyRequestFailureAlert = applyRequestFailure .map { error in DefaultAlertContentVO( @@ -231,24 +232,6 @@ public class NativePostDetailForWorkerVM: BaseViewModel ,NativePostDetailForWork alert.onNext(alertVO) }) .disposed(by: disposeBag) - - // MARK: 로딩 - Observable - .merge(loadingStartObservables) - .subscribe(onNext: { [weak self] _ in - guard let self else { return } - showLoading.onNext(()) - }) - .disposed(by: disposeBag) - - Observable - .merge(loadingEndObservables) - .delay(.milliseconds(500), scheduler: MainScheduler.instance) - .subscribe(onNext: { [weak self] _ in - guard let self else { return } - dismissLoading.onNext(()) - }) - .disposed(by: disposeBag) } public func setPostFavoriteState(isFavoriteRequest: Bool, postId: String, postType: Entity.RecruitmentPostType) -> RxSwift.Single { diff --git a/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift b/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift index e641e0bc..74569bb7 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift @@ -77,6 +77,7 @@ open class BaseViewModel { public func mapStartLoading(_ target: Observable) -> Observable { target + .throttle(.milliseconds(500), scheduler: MainScheduler.instance) .map { [weak self] item in self?.showLoading.onNext(()) diff --git a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift index ea58c765..ae45e355 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift @@ -94,9 +94,6 @@ public class WorkerRecruitmentPostBoardVM: BaseViewModel, WorkerRecruitmentPostB super.init() - var loadingStartObservables: [Observable] = [] - var loadingEndObservables: [Observable] = [] - // MARK: 상단 위치정보 불러오기 workerLocationTitleText = requestWorkerLocation .flatMap { [workerProfileUseCase] _ in @@ -167,7 +164,7 @@ public class WorkerRecruitmentPostBoardVM: BaseViewModel, WorkerRecruitmentPostB } // MARK: 공고리스트 처음부터 요청하기 - let initialRequest = requestInitialPageRequest + let initialRequest = mapEndLoading(mapStartLoading(requestInitialPageRequest.asObservable()) .flatMap { [weak self, recruitmentPostUseCase] request in self?.currentPostVO.accept([]) @@ -178,12 +175,9 @@ public class WorkerRecruitmentPostBoardVM: BaseViewModel, WorkerRecruitmentPostB request: .initial, postCount: 10 ) - } + }) .share() - // 로딩 시작 - loadingStartObservables.append(initialRequest.map { _ in }) - // MARK: 공고리스트 페이징 요청 let pagingRequest = requestNextPage .compactMap { [weak self] _ in @@ -203,9 +197,6 @@ public class WorkerRecruitmentPostBoardVM: BaseViewModel, WorkerRecruitmentPostB .merge(initialRequest, pagingRequest) .share() - // 로딩 종료 - loadingEndObservables.append(postPageReqeustResult.map { _ in }) - let requestPostListSuccess = postPageReqeustResult.compactMap { $0.value } let requestPostListFailure = postPageReqeustResult.compactMap { $0.error } @@ -288,19 +279,6 @@ public class WorkerRecruitmentPostBoardVM: BaseViewModel, WorkerRecruitmentPostB ) .subscribe(self.alert) .disposed(by: dispostBag) - - // MARK: 로딩 - Observable - .merge(loadingStartObservables) - .subscribe(self.showLoading) - .disposed(by: dispostBag) - - - Observable - .merge(loadingEndObservables) - .delay(.milliseconds(300), scheduler: MainScheduler.instance) - .subscribe(self.dismissLoading) - .disposed(by: dispostBag) } func convertStringToLessThan3Words(target: String) -> String { From 2de0042bb526224c3850ff999a3e28811c22b29d Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Fri, 13 Sep 2024 00:19:12 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[IDLE-000]=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=EC=97=90=20=EC=82=AC=EC=9A=A9=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EC=84=BC=ED=84=B0=EC=A0=95=EB=B3=B4=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=A4=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AppliedAndLikedBoardCoordinator.swift | 3 +- .../WorkerRecruitmentBoardCoordinator.swift | 3 +- .../PostDetailForWorkerCoodinator.swift | 10 +++- .../NativePostDetailForWorkerVC.swift | 34 ++++++----- .../Worker/Detail/View/SelectCSTypeVC.swift | 9 ++- .../NativePostDetailForWorkerVM.swift | 59 ++++++++++++++++++- 6 files changed, 93 insertions(+), 25 deletions(-) diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift index 711e5ecb..40062b20 100644 --- a/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift @@ -74,7 +74,8 @@ extension AppliedAndLikedBoardCoordinator { parent: self, navigationController: navigationController, recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self), - workerProfileUseCase: injector.resolve(WorkerProfileUseCase.self) + workerProfileUseCase: injector.resolve(WorkerProfileUseCase.self), + centerProfileUseCase: injector.resolve(CenterProfileUseCase.self) ) ) addChildCoordinator(coodinator) diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift index 47605e5d..5111c7b1 100644 --- a/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift @@ -63,7 +63,8 @@ extension WorkerRecruitmentBoardCoordinator { parent: self, navigationController: navigationController, recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self), - workerProfileUseCase: injector.resolve(WorkerProfileUseCase.self) + workerProfileUseCase: injector.resolve(WorkerProfileUseCase.self), + centerProfileUseCase: injector.resolve(CenterProfileUseCase.self) ) ) addChildCoordinator(coodinator) diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/Coordinator/PostDetailForWorkerCoodinator.swift b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/Coordinator/PostDetailForWorkerCoodinator.swift index 204d0555..469e3499 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/Coordinator/PostDetailForWorkerCoodinator.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/Coordinator/PostDetailForWorkerCoodinator.swift @@ -19,6 +19,7 @@ public class PostDetailForWorkerCoodinator: ChildCoordinator { let navigationController: UINavigationController let recruitmentPostUseCase: RecruitmentPostUseCase let workerProfileUseCase: WorkerProfileUseCase + let centerProfileUseCase: CenterProfileUseCase public init( postType: RecruitmentPostType, @@ -26,7 +27,8 @@ public class PostDetailForWorkerCoodinator: ChildCoordinator { parent: WorkerRecruitmentBoardCoordinatable? = nil, navigationController: UINavigationController, recruitmentPostUseCase: RecruitmentPostUseCase, - workerProfileUseCase: WorkerProfileUseCase + workerProfileUseCase: WorkerProfileUseCase, + centerProfileUseCase: CenterProfileUseCase ) { self.postType = postType self.postId = postId @@ -34,6 +36,7 @@ public class PostDetailForWorkerCoodinator: ChildCoordinator { self.navigationController = navigationController self.recruitmentPostUseCase = recruitmentPostUseCase self.workerProfileUseCase = workerProfileUseCase + self.centerProfileUseCase = centerProfileUseCase } } @@ -45,6 +48,7 @@ public class PostDetailForWorkerCoodinator: ChildCoordinator { public let navigationController: UINavigationController let recruitmentPostUseCase: RecruitmentPostUseCase let workerProfileUseCase: WorkerProfileUseCase + let centerProfileUseCase: CenterProfileUseCase public init( dependency: Dependency @@ -55,6 +59,7 @@ public class PostDetailForWorkerCoodinator: ChildCoordinator { self.navigationController = dependency.navigationController self.recruitmentPostUseCase = dependency.recruitmentPostUseCase self.workerProfileUseCase = dependency.workerProfileUseCase + self.centerProfileUseCase = dependency.centerProfileUseCase } deinit { @@ -72,7 +77,8 @@ public class PostDetailForWorkerCoodinator: ChildCoordinator { postId: postId, coordinator: self, recruitmentPostUseCase: recruitmentPostUseCase, - workerProfileUseCase: workerProfileUseCase + workerProfileUseCase: workerProfileUseCase, + centerProfileUseCase: centerProfileUseCase ) nativeDetailVC.bind(viewModel: vm) vc = nativeDetailVC diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift index c9568c07..9ed9bb2d 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift @@ -47,7 +47,6 @@ public class NativePostDetailForWorkerVC: BaseViewController { public override func viewDidLoad() { setAppearance() setLayout() - setObservable() } private func setAppearance() { @@ -105,24 +104,14 @@ public class NativePostDetailForWorkerVC: BaseViewController { buttonStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16), ]) } - - private func setObservable() { - - // '문의하기'버튼 클릭시 - csButton.rx.tap - .subscribe { [weak self] _ in - let vc = SelectCSTypeVC() - vc.modalPresentationStyle = .overFullScreen - self?.present(vc, animated: false) - } - .disposed(by: disposeBag) - } - + public func bind(viewModel: NativePostDetailForWorkerViewModelable) { super.bind(viewModel: viewModel) // Output + let centerPhoneNumber: PublishSubject = .init() + viewModel .postForWorkerBundle? .drive(onNext: { @@ -203,6 +192,22 @@ public class NativePostDetailForWorkerVC: BaseViewController { }) .disposed(by: disposeBag) + // 문의하기 버튼 + if let centerInfo = viewModel.centerInfoForCS?.asObservable() { + csButton.rx.tap + .withLatestFrom(centerInfo.asObservable()) + .subscribe (onNext: { [weak self] info in + let vc = SelectCSTypeVC() + vc.phoneCSButton.bind( + nameText: info.name, + phoneNumberText: info.phoneNumber + ) + vc.modalPresentationStyle = .overFullScreen + self?.present(vc, animated: false) + }) + .disposed(by: disposeBag) + } + // 지원성공시 비활성화 viewModel .applySuccess? @@ -248,6 +253,7 @@ public class NativePostDetailForWorkerVC: BaseViewController { .rx.tap .bind(to: viewModel.backButtonClicked) .disposed(by: disposeBag) + } } diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/SelectCSTypeVC.swift b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/SelectCSTypeVC.swift index af1cf62f..cf6d59c7 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/SelectCSTypeVC.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/SelectCSTypeVC.swift @@ -12,21 +12,21 @@ import RxSwift import Entity import DSKit -public class SelectCSTypeVC: IdleBottomSheetVC { +class SelectCSTypeVC: IdleBottomSheetVC { // Init // View let phoneCSButton: PhoneCSButton = .init() - public override init() { + override init() { super.init() setObservable() } - public required init?(coder: NSCoder) { fatalError() } + required init?(coder: NSCoder) { fatalError() } - public override func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() setLayout() @@ -51,7 +51,6 @@ public class SelectCSTypeVC: IdleBottomSheetVC { private func setObservable() { } - } @available(iOS 17.0, *) diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/ViewModel/NativePostDetailForWorkerVM.swift b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/ViewModel/NativePostDetailForWorkerVM.swift index 978b2b0b..a5d44281 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/ViewModel/NativePostDetailForWorkerVM.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/ViewModel/NativePostDetailForWorkerVM.swift @@ -15,12 +15,15 @@ import DSKit public protocol NativePostDetailForWorkerViewModelable: BaseViewModel { + typealias CenterInfoForCS = (name: String, phoneNumber: String) + // Output var postForWorkerBundle: Driver? { get } var locationInfo: Driver? { get } var idleAlertVM: Driver? { get } var starButtonRequestResult: Driver? { get } var applySuccess: Driver? { get } + var centerInfoForCS: Driver? { get } // Input var viewWillAppear: PublishRelay { get } @@ -39,6 +42,7 @@ public class NativePostDetailForWorkerVM: BaseViewModel ,NativePostDetailForWork private let postId: String private let recruitmentPostUseCase: RecruitmentPostUseCase private let workerProfileUseCase: WorkerProfileUseCase + private let centerProfileUseCase: CenterProfileUseCase // Ouput public var postForWorkerBundle: RxCocoa.Driver? @@ -46,6 +50,7 @@ public class NativePostDetailForWorkerVM: BaseViewModel ,NativePostDetailForWork public var idleAlertVM: Driver? public var starButtonRequestResult: Driver? public var applySuccess: RxCocoa.Driver? + public var centerInfoForCS: Driver? // Input public var backButtonClicked: RxRelay.PublishRelay = .init() @@ -54,18 +59,21 @@ public class NativePostDetailForWorkerVM: BaseViewModel ,NativePostDetailForWork public var centerCardClicked: RxRelay.PublishRelay = .init() public var viewWillAppear: RxRelay.PublishRelay = .init() + private var centerInfoForCSCache: CenterInfoForCS? public init( postId: String, coordinator: PostDetailForWorkerCoodinator?, recruitmentPostUseCase: RecruitmentPostUseCase, - workerProfileUseCase: WorkerProfileUseCase + workerProfileUseCase: WorkerProfileUseCase, + centerProfileUseCase: CenterProfileUseCase ) { self.postId = postId self.coordinator = coordinator self.recruitmentPostUseCase = recruitmentPostUseCase self.workerProfileUseCase = workerProfileUseCase + self.centerProfileUseCase = centerProfileUseCase super.init() @@ -128,6 +136,44 @@ public class NativePostDetailForWorkerVM: BaseViewModel ,NativePostDetailForWork } .asDriver(onErrorRecover: { _ in fatalError() }) + // MARK: 센터전화번호 가져오기 + let centerInfoCache = viewWillAppear.compactMap { [weak self] _ in + self?.centerInfoForCSCache + } + + let centerProfileRequestResult = getPostDetailSuccess + .filter { [weak self] _ in + self?.centerInfoForCSCache == nil + } + .flatMap { [centerProfileUseCase] bundle in + + let centerId = bundle.centerInfo.centerId + + return centerProfileUseCase + .getProfile(mode: .otherProfile(id: centerId)) + } + .share() + + let centerProfileRequestSuccess = centerProfileRequestResult.compactMap { $0.value } + let centerProfileRequestFailure = centerProfileRequestResult.compactMap { $0.error } + + let centerInfoForCS = centerProfileRequestSuccess + .map { [weak self] profile in + + let info = (profile.centerName, profile.officeNumber) as CenterInfoForCS + self?.centerInfoForCSCache = info + + return info + } + + self.centerInfoForCS = Observable + .merge( + centerInfoForCS, + centerInfoCache + ) + .asDriver(onErrorDriveWith: .never()) + + // MARK: 버튼 처리 backButtonClicked .subscribe(onNext: { [weak self] _ in @@ -196,6 +242,14 @@ public class NativePostDetailForWorkerVM: BaseViewModel ,NativePostDetailForWork message: error.message ) } + + let centerProfileRequestFailureAlert = centerProfileRequestFailure + .map { error in + DefaultAlertContentVO( + title: "센터정보 불러오기 실패", + message: error.message + ) + } // MARK: 즐겨찾기 starButtonRequestResult = starButtonClicked @@ -225,7 +279,8 @@ public class NativePostDetailForWorkerVM: BaseViewModel ,NativePostDetailForWork .merge( getPostDetailFailureAlert, applyRequestFailureAlert, - requestWorkerLocationFailureAlert + requestWorkerLocationFailureAlert, + centerProfileRequestFailureAlert ) .subscribe(onNext: { [weak self] alertVO in guard let self else { return } From bc78daeccd7bda7b905ea26c67a7cc1a713cb22c Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Fri, 13 Sep 2024 00:31:12 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[IDLE-000]=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=ED=81=B4=EB=A6=AD=EC=8B=9C=20=EC=A0=84=ED=99=94?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=EB=A1=9C=20=EC=A0=84=ED=99=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DSKit/Sources/CommonUI/Button/PhoneCSButton.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/Button/PhoneCSButton.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/Button/PhoneCSButton.swift index 8dbc5005..66776653 100644 --- a/project/Projects/Presentation/DSKit/Sources/CommonUI/Button/PhoneCSButton.swift +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/Button/PhoneCSButton.swift @@ -107,6 +107,16 @@ public class PhoneCSButton: TappableUIView { self.nameLabel.textString = nameText self.phoneNumberLabel.textString = phoneNumberText + + self + .rx.tap + .subscribe(onNext: { + + if let phoneURL = URL(string: "tel://\(phoneNumberText)"), UIApplication.shared.canOpenURL(phoneURL) { + UIApplication.shared.open(phoneURL, options: [:], completionHandler: nil) + } + }) + .disposed(by: disposeBag) } } From 53bccf8e72ee93779ca6d45da30ecf2707555fd1 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Fri, 13 Sep 2024 00:35:23 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[IDLE-000]=20=EC=A7=80=EC=9B=90=ED=95=9C=20?= =?UTF-8?q?=EA=B3=B5=EA=B3=A0=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=20=EB=B8=94=EB=A1=9C=ED=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/Postdetail/NativePostDetailForWorkerVC.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift index 9ed9bb2d..768c6bd5 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/View/RecruitmentPost/Worker/Detail/View/Postdetail/NativePostDetailForWorkerVC.swift @@ -35,6 +35,7 @@ public class NativePostDetailForWorkerVC: BaseViewController { let applyButton: IdlePrimaryButton = { let btn = IdlePrimaryButton(level: .medium) btn.label.textString = "지원하기" + btn.setEnabled(false) return btn }() @@ -110,8 +111,6 @@ public class NativePostDetailForWorkerVC: BaseViewController { super.bind(viewModel: viewModel) // Output - let centerPhoneNumber: PublishSubject = .init() - viewModel .postForWorkerBundle? .drive(onNext: { @@ -124,6 +123,14 @@ public class NativePostDetailForWorkerVC: BaseViewController { contentView.cardView.bind(ro: cardRO) + if bundle.applyDate != nil { + // 지원한 공고인 경우 + applyButton.setEnabled(false) + } else { + // 지원하지 않은 공고인 경우 + applyButton.setEnabled(true) + } + // 근무 조건 contentView.workConditionView.bind( workTimeAndPayStateObject: bundle.workTimeAndPay,