diff --git a/project/Projects/Data/ConcreteRepository/Auth/DefaultAuthRepository.swift b/project/Projects/Data/ConcreteRepository/Auth/DefaultAuthRepository.swift index e0440f27..105d8d6d 100644 --- a/project/Projects/Data/ConcreteRepository/Auth/DefaultAuthRepository.swift +++ b/project/Projects/Data/ConcreteRepository/Auth/DefaultAuthRepository.swift @@ -54,9 +54,9 @@ public extension DefaultAuthRepository { .map { _ in } } - func deregisterCenterAccount(reasons: [DeregisterReasonVO], password: String) -> RxSwift.Single { + func deregisterCenterAccount(reasons: [String], password: String) -> RxSwift.Single { - let reasonString = reasons.map { $0.reasonText }.joined(separator: "|") + let reasonString = reasons.joined(separator: "|") return networkService .request( @@ -119,8 +119,8 @@ public extension DefaultAuthRepository { .map { _ in } } - func deregisterWorkerAccount(reasons: [Entity.DeregisterReasonVO]) -> RxSwift.Single { - let reasonString = reasons.map { $0.reasonText }.joined(separator: "|") + func deregisterWorkerAccount(reasons: [String]) -> RxSwift.Single { + let reasonString = reasons.joined(separator: "|") return networkService .request( diff --git a/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift b/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift index 618880de..56b26345 100644 --- a/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift @@ -94,7 +94,7 @@ public class DefaultSettingUseCase: SettingScreenUseCase { // MARK: 회원탈퇴 & 로그아웃 // 센터 회원탈퇴 - public func deregisterCenterAccount(reasons: [Entity.DeregisterReasonVO], password: String) -> RxSwift.Single> { + public func deregisterCenterAccount(reasons: [String], password: String) -> RxSwift.Single> { let task = authRepository .deregisterCenterAccount(reasons: reasons, password: password) .map { [weak self] _ in @@ -118,7 +118,7 @@ public class DefaultSettingUseCase: SettingScreenUseCase { } // 요양보호사 회원탈퇴 - public func deregisterWorkerAccount(reasons: [Entity.DeregisterReasonVO]) -> RxSwift.Single> { + public func deregisterWorkerAccount(reasons: [String]) -> RxSwift.Single> { let task = authRepository .deregisterWorkerAccount(reasons: reasons) .map { [weak self] _ in diff --git a/project/Projects/Domain/ConcreteUseCase/Setting/SettingAdditionalInfoType.swift b/project/Projects/Domain/ConcreteUseCase/Setting/SettingAdditionalInfoType.swift new file mode 100644 index 00000000..de662d83 --- /dev/null +++ b/project/Projects/Domain/ConcreteUseCase/Setting/SettingAdditionalInfoType.swift @@ -0,0 +1,32 @@ +// +// SettingAdditionalInfoType.swift +// Entity +// +// Created by choijunios on 9/15/24. +// + +import Foundation + +public enum SettingAdditionalInfoType { + + case frequentQuestion + case contact + case termsandPolicies + case privacyPolicy + + public func getWebUrl() -> URL { + var urlString: String! + switch self { + case .frequentQuestion: + urlString = "https://grove-maraca-55d.notion.site/8579186ee8ca4dbb8dc55e3b8b744d11?pvs=4" + case .contact: + urlString = "https://docs.google.com/forms/d/1I1N9xmYuqlpy8K1ga8xOECPZl-1sW33WrKIzXas74Is/viewform?edit_requested=true" + case .termsandPolicies: + urlString = "https://grove-maraca-55d.notion.site/2e4d597aff1f406e9164cdb6f9195de0?pvs=4" + case .privacyPolicy: + urlString = "https://grove-maraca-55d.notion.site/ad4f62dff5304d63a162f1269639afca?pvs=4" + } + + return URL(string: urlString)! + } +} diff --git a/project/Projects/Domain/RepositoryInterface/Auth/AuthRepository.swift b/project/Projects/Domain/RepositoryInterface/Auth/AuthRepository.swift index cf433a21..eaa8ad69 100644 --- a/project/Projects/Domain/RepositoryInterface/Auth/AuthRepository.swift +++ b/project/Projects/Domain/RepositoryInterface/Auth/AuthRepository.swift @@ -14,7 +14,7 @@ public protocol AuthRepository: RepositoryBase { func requestRegisterCenterAccount(managerName: String, phoneNumber: String, businessNumber: String, id: String, password: String) -> Single func requestCenterLogin(id: String, password: String) -> Single func signoutCenterAccount() -> Single - func deregisterCenterAccount(reasons: [DeregisterReasonVO], password: String) -> Single + func deregisterCenterAccount(reasons: [String], password: String) -> Single func getCenterJoinStatus() -> Single func requestCenterJoin() -> Single func setNewPassword(phoneNumber: String, password: String) -> Single @@ -24,5 +24,5 @@ public protocol AuthRepository: RepositoryBase { func requestRegisterWorkerAccount(registerState: WorkerRegisterState) -> Single func requestWorkerLogin(phoneNumber: String, authNumber: String) -> Single func signoutWorkerAccount() -> Single - func deregisterWorkerAccount(reasons: [DeregisterReasonVO]) -> Single + func deregisterWorkerAccount(reasons: [String]) -> Single } diff --git a/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift b/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift index fa6e1e62..57a0a2f7 100644 --- a/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift +++ b/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift @@ -35,7 +35,7 @@ public protocol SettingScreenUseCase: BaseUseCase { /// 요양보호사 회원 탈퇴 func deregisterWorkerAccount( - reasons: [DeregisterReasonVO] + reasons: [String] ) -> Single> /// 요양보호사 로그아웃 @@ -48,7 +48,7 @@ public protocol SettingScreenUseCase: BaseUseCase { /// 센터 회원 탈퇴 func deregisterCenterAccount( - reasons: [DeregisterReasonVO], + reasons: [String], password: String ) -> Single> diff --git a/project/Projects/Presentation/DSKit/Sources/Component/TextField/MultiLineTextField.swift b/project/Projects/Presentation/DSKit/Sources/Component/TextField/MultiLineTextField.swift index 3ba6665b..838b1eb5 100644 --- a/project/Projects/Presentation/DSKit/Sources/Component/TextField/MultiLineTextField.swift +++ b/project/Projects/Presentation/DSKit/Sources/Component/TextField/MultiLineTextField.swift @@ -18,6 +18,8 @@ public class MultiLineTextField: UITextView { let label = IdleLabel(typography: typography) label.attrTextColor = DSKitAsset.Colors.gray200.color label.textAlignment = .left + label.numberOfLines = 0 + label.lineBreakMode = .byCharWrapping return label }() @@ -88,10 +90,12 @@ public class MultiLineTextField: UITextView { self.addSubview(placeHolderLabel) placeHolderLabel.translatesAutoresizingMaskIntoConstraints = false + let frameGuide = self.frameLayoutGuide + let textContainerInset = self.textContainerInset NSLayoutConstraint.activate([ - placeHolderLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: self.textContainerInset.top), - placeHolderLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: self.textContainerInset.left), - placeHolderLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: self.textContainerInset.right), + placeHolderLabel.topAnchor.constraint(equalTo: frameGuide.topAnchor, constant: textContainerInset.top), + placeHolderLabel.leftAnchor.constraint(equalTo: frameGuide.leftAnchor, constant: textContainerInset.left), + placeHolderLabel.rightAnchor.constraint(equalTo: frameGuide.rightAnchor, constant: -textContainerInset.right), ]) } diff --git a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift index a235be57..aa53ce9e 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift @@ -14,10 +14,10 @@ public class PasswordForDeregisterCoordinator: ChildCoordinator { public struct Dependency { let settingUseCase: SettingScreenUseCase - let reasons: [DeregisterReasonVO] + let reasons: [String] let navigationController: UINavigationController - public init(settingUseCase: SettingScreenUseCase, reasons: [DeregisterReasonVO], navigationController: UINavigationController) { + public init(settingUseCase: SettingScreenUseCase, reasons: [String], navigationController: UINavigationController) { self.settingUseCase = settingUseCase self.reasons = reasons self.navigationController = navigationController @@ -29,7 +29,7 @@ public class PasswordForDeregisterCoordinator: ChildCoordinator { public let navigationController: UINavigationController let settingUseCase: SettingScreenUseCase - let reasons: [DeregisterReasonVO] + let reasons: [String] public init( dependency: Dependency diff --git a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift index 8d78b18b..b52363c5 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift @@ -101,6 +101,21 @@ public class CenterSettingVC: BaseViewController { .bind(to: viewModel.removeAccountButtonClicked) .disposed(by: disposeBag) + // 설정화면에 종속적인 뷰들입니다. + Observable + .merge( + frequentQuestionButton.rx.tap + .map { _ in SettingAdditionalInfoType.frequentQuestion }, + askButton.rx.tap + .map { _ in SettingAdditionalInfoType.contact }, + applicationPolicyButton.rx.tap + .map { _ in SettingAdditionalInfoType.termsandPolicies }, + personalDataProcessingPolicyButton.rx.tap + .map { _ in SettingAdditionalInfoType.privacyPolicy } + ) + .bind(to: viewModel.additionalInfoButtonClieck) + .disposed(by: disposeBag) + // Output viewModel .pushNotificationApproveState? @@ -216,39 +231,6 @@ public class CenterSettingVC: BaseViewController { private func setObservable() { - // 설정화면에 종속적인 뷰들입니다. - frequentQuestionButton.rx.tap - .subscribe(onNext: { - - // 자주하는 질문뷰 - - }) - .disposed(by: disposeBag) - - askButton.rx.tap - .subscribe(onNext: { - - // 문의하기 뷰 - - }) - .disposed(by: disposeBag) - - applicationPolicyButton.rx.tap - .subscribe(onNext: { - - // 약관및 정책 - - }) - .disposed(by: disposeBag) - - personalDataProcessingPolicyButton.rx.tap - .subscribe(onNext: { - - // 개인정보 처리방침 - - }) - .disposed(by: disposeBag) - signOutButton .rx.tap .subscribe(onNext: { [weak self] _ in diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift index a1b74149..c0777fad 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift @@ -25,6 +25,8 @@ public protocol CenterSettingVMable: BaseViewModel { var signOutButtonComfirmed: PublishRelay { get } var removeAccountButtonClicked: PublishRelay { get } + var additionalInfoButtonClieck: PublishRelay { get } + // Output var pushNotificationApproveState: Driver? { get } var showSettingAlert: Driver? { get } @@ -40,12 +42,14 @@ public class CenterSettingVM: BaseViewModel, CenterSettingVMable { weak var coordinator: CenterSettingScreenCoordinator? let settingUseCase: SettingScreenUseCase - public var viewWillAppear: RxRelay.PublishRelay = .init() - public var myCenterProfileButtonClicked: RxRelay.PublishRelay = .init() - public var approveToPushNotification: RxRelay.PublishRelay = .init() + public var viewWillAppear: PublishRelay = .init() + public var myCenterProfileButtonClicked: PublishRelay = .init() + public var approveToPushNotification: PublishRelay = .init() + + public var signOutButtonComfirmed: PublishRelay = .init() + public var removeAccountButtonClicked: PublishRelay = .init() - public var signOutButtonComfirmed: RxRelay.PublishRelay = .init() - public var removeAccountButtonClicked: RxRelay.PublishRelay = .init() + public let additionalInfoButtonClieck: PublishRelay = .init() public var pushNotificationApproveState: RxCocoa.Driver? public var centerInfo: RxCocoa.Driver<(name: String, location: String)>? @@ -125,6 +129,26 @@ public class CenterSettingVM: BaseViewModel, CenterSettingVMable { }) .disposed(by: disposeBag) + // MARK: 추가정보 + additionalInfoButtonClieck + .subscribe(onNext: { + [weak self] type in + guard let self else { return } + + let url = type.getWebUrl() + + if !openURL(url) { + + alert.onNext( + .init( + title: "오류가 발생했습니다.", + message: "잠시후 다시시도해 주세요." + ) + ) + } + }) + .disposed(by: disposeBag) + // MARK: 로그아웃 let signOutRequestResult = signOutButtonComfirmed.flatMap({ [settingUseCase] _ in @@ -181,6 +205,14 @@ public class CenterSettingVM: BaseViewModel, CenterSettingVMable { return viewModel } + + private func openURL(_ url: URL) -> Bool { + if UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url, options: [:]) + return true + } + return false + } } class CenterSingOutVM: IdleAlertViewModelable { diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift index e842ee73..70cafdbd 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift @@ -22,7 +22,7 @@ public class PasswordForDeregisterVM: BaseViewModel { let settingUseCase: SettingScreenUseCase public init( - deregisterReasons: [DeregisterReasonVO], + deregisterReasons: [String], coordinator: PasswordForDeregisterCoordinator, settingUseCase: SettingScreenUseCase ) { diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift index ad1a8a9c..ed24abe0 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift @@ -62,7 +62,7 @@ public class DeRegisterCoordinator: DeregisterCoordinatable { coordinator.start() } - public func showFinalPasswordScreen(reasons: [Entity.DeregisterReasonVO]) { + public func showFinalPasswordScreen(reasons: [String]) { let coordinator = PasswordForDeregisterCoordinator( dependency: .init( @@ -76,7 +76,7 @@ public class DeRegisterCoordinator: DeregisterCoordinatable { coordinator.start() } - public func showFinalPhoneAuthScreen(reasons: [Entity.DeregisterReasonVO]) { + public func showFinalPhoneAuthScreen(reasons: [String]) { let coordinator = PhoneNumberValidationForDeregisterCoordinator( dependency: .init( settingUseCase: settingUseCase, diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift index b69db6c1..85f91125 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift @@ -61,11 +61,11 @@ public class SelectReasonCoordinator: ChildCoordinator { parent?.removeChildCoordinator(self) } - public func showPasswordAuthScreen(reasons: [DeregisterReasonVO]) { + public func showPasswordAuthScreen(reasons: [String]) { parent?.showFinalPasswordScreen(reasons: reasons) } - public func showPhoneNumberAuthScreen(reasons: [DeregisterReasonVO]) { + public func showPhoneNumberAuthScreen(reasons: [String]) { parent?.showFinalPhoneAuthScreen(reasons: reasons) } } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/View/DeregisterReasonVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/View/DeregisterReasonVC.swift index 88f012ce..ce4d25f1 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/View/DeregisterReasonVC.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/View/DeregisterReasonVC.swift @@ -15,9 +15,10 @@ import DSKit import Entity public protocol DeregisterReasonVMable: BaseViewModel { + var userType: UserType { get } var coordinator: SelectReasonCoordinator? { get } var exitButonClicked: PublishRelay { get } - var acceptDeregisterButonClicked: PublishRelay<[DeregisterReasonVO]> { get } + var acceptDeregisterButonClicked: PublishRelay<[String]> { get } } public class DeregisterReasonVC: BaseViewController { @@ -43,7 +44,10 @@ public class DeregisterReasonVC: BaseViewController { label.attrTextColor = DSColor.gray300.color return label }() - var itemViewList: [CheckBoxWithLabelView] = { + + private let mainScrollView = UIScrollView() + + let itemViewList: [CheckBoxWithLabelView] = { DeregisterReasonVO.allCases.map { vo in return CheckBoxWithLabelView( item: vo, @@ -56,6 +60,7 @@ public class DeregisterReasonVC: BaseViewController { let label = IdleLabel(typography: .caption) label.textString = "탈퇴 버튼 선택 시 모든 정보가 삭제되며, 되돌릴 수 없습니다." label.attrTextColor = DSColor.red100.color + label.textAlignment = .center return label }() @@ -72,6 +77,24 @@ public class DeregisterReasonVC: BaseViewController { return button }() + fileprivate let deregisterReasonField1: DeregisterInputField = { + let field = DeregisterInputField( + titleText: "어떤 부분에서 불편함을 느끼셨나요?", + fieldText: "어떤 부분에서 불편함을 느끼셨나요? 보내주신 의견은 플랫폼 개선에 큰 도움이 됩니다!" + ) + field.isHidden = true + return field + }() + + fileprivate let deregisterReasonField2: DeregisterInputField = { + let field = DeregisterInputField( + titleText: "어떤 기능이 필요하신가요?", + fieldText: "어떤 기능이 필요하신가요? 보내주신 의견은 개발 담당자에게 즉시 전달됩니다." + ) + field.isHidden = true + return field + }() + // Observable private var selectedReasons: [DeregisterReasonVO: Bool] = { var dict: [DeregisterReasonVO: Bool] = [:] @@ -92,6 +115,7 @@ public class DeregisterReasonVC: BaseViewController { setAppearance() setLayout() setObservable() + setKeyboardAvoidance() } private func setAppearance() { @@ -99,27 +123,58 @@ public class DeregisterReasonVC: BaseViewController { } private func setLayout() { - let checkListScrollView = UIScrollView() - checkListScrollView.contentInset.top = 32 - let contentGuide = checkListScrollView.contentLayoutGuide - let frameGuide = checkListScrollView.frameLayoutGuide - let contentStack = VStack(itemViewList, spacing: 16, alignment: .fill) - checkListScrollView.addSubview(contentStack) - contentStack.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - contentStack.topAnchor.constraint(equalTo: contentGuide.topAnchor), - contentStack.bottomAnchor.constraint(equalTo: contentGuide.bottomAnchor, constant: -20), - - contentStack.leftAnchor.constraint(equalTo: frameGuide.leftAnchor, constant: 20), - contentStack.rightAnchor.constraint(equalTo: frameGuide.rightAnchor, constant: 20), - ]) + mainScrollView.contentInset.top = 36 + mainScrollView.contentInset.bottom = 12 + + let contentView: UIView = .init() + let contentGuide = mainScrollView.contentLayoutGuide + let frameGuide = mainScrollView.frameLayoutGuide + + // MARK: 체크리스트 + 텍스트필드 + let checkListStack = VStack(itemViewList, spacing: 16, alignment: .fill) + + // inputField 삽입 + checkListStack.insertArrangedSubview(deregisterReasonField1, at: 2) + checkListStack.insertArrangedSubview(deregisterReasonField2, at: 6) let titleLabelStack = VStack([ titleLabel, subTitleLabel ], spacing: 8, alignment: .leading) + [ + titleLabelStack, + checkListStack, + finalWarningLabel, + ].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview($0) + } + + mainScrollView.addSubview(contentView) + contentView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + + titleLabelStack.topAnchor.constraint(equalTo: contentView.topAnchor), + titleLabelStack.leftAnchor.constraint(equalTo: contentView.leftAnchor), + titleLabelStack.rightAnchor.constraint(equalTo: contentView.rightAnchor), + + checkListStack.topAnchor.constraint(equalTo: titleLabelStack.bottomAnchor, constant: 32), + checkListStack.leftAnchor.constraint(equalTo: contentView.leftAnchor), + checkListStack.rightAnchor.constraint(equalTo: contentView.rightAnchor), + checkListStack.bottomAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: -8), + + // contentView - ScrollView + contentView.topAnchor.constraint(equalTo: contentGuide.topAnchor), + contentView.bottomAnchor.constraint(equalTo: contentGuide.bottomAnchor), + + contentView.leftAnchor.constraint(equalTo: frameGuide.leftAnchor, constant: 20), + contentView.rightAnchor.constraint(equalTo: frameGuide.rightAnchor, constant: -20), + ]) + + // MARK: 하단 버튼 let buttonStack = HStack( [ cancelButton, @@ -129,13 +184,17 @@ public class DeregisterReasonVC: BaseViewController { alignment: .center, distribution: .fillEqually ) - - [ - navigationBar, - titleLabelStack, - checkListScrollView, + + let bottomStack = VStack([ finalWarningLabel, buttonStack + ], spacing: 12, alignment: .fill) + + // MARK: main + [ + navigationBar, + mainScrollView, + bottomStack, ].forEach { $0.translatesAutoresizingMaskIntoConstraints = false view.addSubview($0) @@ -146,21 +205,14 @@ public class DeregisterReasonVC: BaseViewController { navigationBar.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), navigationBar.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), - titleLabelStack.topAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: 24), - titleLabelStack.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 20), - titleLabelStack.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -20), - - checkListScrollView.topAnchor.constraint(equalTo: titleLabelStack.bottomAnchor), - checkListScrollView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), - checkListScrollView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), - checkListScrollView.bottomAnchor.constraint(equalTo: finalWarningLabel.bottomAnchor), - - finalWarningLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), - finalWarningLabel.bottomAnchor.constraint(equalTo: buttonStack.topAnchor, constant: -12), + mainScrollView.topAnchor.constraint(equalTo: navigationBar.bottomAnchor), + mainScrollView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), + mainScrollView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), + mainScrollView.bottomAnchor.constraint(equalTo: bottomStack.topAnchor), - buttonStack.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 20), - buttonStack.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -20), - buttonStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -14), + bottomStack.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 20), + bottomStack.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -20), + bottomStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -14), ]) } @@ -194,12 +246,46 @@ public class DeregisterReasonVC: BaseViewController { super.bind(viewModel: viewModel) + if viewModel.userType == .worker { + + itemViewList[1] + .opTap + .subscribe { [weak self] _ in + self?.deregisterReasonField1.isHidden.toggle() + } + .disposed(by: disposeBag) + + itemViewList[4] + .opTap + .subscribe { [weak self] _ in + self?.deregisterReasonField2.isHidden.toggle() + } + .disposed(by: disposeBag) + } + acceptDeregisterButton .rx.tap .map { [weak self] _ in - let reasons = self?.selectedReasons.filter({ (reason, isActive) in isActive}).map { (key, _) in - key + + // 선택항목 + var reasons = self?.selectedReasons.filter({ (reason, isActive) in isActive}).map { (key, _) in + key.reasonText + } + + // 불편했던 점 + if let str = self?.deregisterReasonField1.reasonField.attributedText.string, !str.isEmpty { + let formedStr = "불편했던 점: \(str)" + reasons?.append(formedStr) } + + // 필요한 기능 + if let str = self?.deregisterReasonField2.reasonField.attributedText.string, !str.isEmpty { + let formedStr = "필요한 기능: \(str)" + reasons?.append(formedStr) + } + + // 어떤 기능 + return reasons ?? [] } .bind(to: viewModel.acceptDeregisterButonClicked) @@ -213,5 +299,44 @@ public class DeregisterReasonVC: BaseViewController { .bind(to: viewModel.exitButonClicked) .disposed(by: disposeBag) } + + func setKeyboardAvoidance() { + + [ + deregisterReasonField1.reasonField, + deregisterReasonField2.reasonField + ].forEach { (view: IdleKeyboardAvoidable) in + + view.setKeyboardAvoidance(movingView: mainScrollView) + } + } } + +fileprivate class DeregisterInputField: VStack { + + let titleLabel: IdleLabel = { + let label = IdleLabel(typography: .Subtitle4) + label.textAlignment = .left + label.attrTextColor = DSColor.gray500.color + return label + }() + + let reasonField: MultiLineTextField = { + let field = MultiLineTextField(typography: .Body3) + field.isScrollEnabled = false + return field + }() + + init(titleText: String, fieldText: String) { + self.titleLabel.textString = titleText + self.reasonField.placeholderText = fieldText + super.init([titleLabel, reasonField], spacing: 6, alignment: .fill) + + NSLayoutConstraint.activate([ + reasonField.heightAnchor.constraint(equalToConstant: 156) + ]) + } + + required init(coder: NSCoder) { fatalError() } +} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/ViewModel/CenterDeregisterReasonsVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/ViewModel/CenterDeregisterReasonsVM.swift index e2ecdebe..d81c1905 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/ViewModel/CenterDeregisterReasonsVM.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/ViewModel/CenterDeregisterReasonsVM.swift @@ -11,10 +11,11 @@ import RxCocoa import BaseFeature public class CenterDeregisterReasonsVM: BaseViewModel, DeregisterReasonVMable { - + public weak var coordinator: SelectReasonCoordinator? public var exitButonClicked: RxRelay.PublishRelay = .init() - public var acceptDeregisterButonClicked: PublishRelay<[DeregisterReasonVO]> = .init() + public var acceptDeregisterButonClicked: PublishRelay<[String]> = .init() + public var userType: Entity.UserType = .center public init(coordinator: SelectReasonCoordinator) { self.coordinator = coordinator diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/InitialScreen/InitialScreenVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/InitialScreen/InitialScreenVM.swift index 43dc62bc..fdb29375 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/InitialScreen/InitialScreenVM.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/InitialScreen/InitialScreenVM.swift @@ -389,11 +389,6 @@ public class InitialScreenVM: BaseViewModel { .disposed(by: disposeBag) } - func checkForceUpdate() { - - - } - /// 앱스토에에서 해당앱을 엽니다. func openAppStoreForUpdate() { let url = "itms-apps://itunes.apple.com/app/6670529341"; diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/PhoneNumberValidationForDeregisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/PhoneNumberValidationForDeregisterCoordinator.swift index 72b5aaf3..5330b78f 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/PhoneNumberValidationForDeregisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/PhoneNumberValidationForDeregisterCoordinator.swift @@ -15,10 +15,10 @@ class PhoneNumberValidationForDeregisterCoordinator: ChildCoordinator { struct Dependency { let settingUseCase: SettingScreenUseCase let inputValidationUseCase: AuthInputValidationUseCase - let reasons: [DeregisterReasonVO] + let reasons: [String] let navigationController: UINavigationController - init(settingUseCase: SettingScreenUseCase, inputValidationUseCase: AuthInputValidationUseCase, reasons: [DeregisterReasonVO], navigationController: UINavigationController) { + init(settingUseCase: SettingScreenUseCase, inputValidationUseCase: AuthInputValidationUseCase, reasons: [String], navigationController: UINavigationController) { self.settingUseCase = settingUseCase self.inputValidationUseCase = inputValidationUseCase self.reasons = reasons @@ -28,7 +28,7 @@ class PhoneNumberValidationForDeregisterCoordinator: ChildCoordinator { let settingUseCase: SettingScreenUseCase let inputValidationUseCase: AuthInputValidationUseCase - let reasons: [DeregisterReasonVO] + let reasons: [String] weak var viewControllerRef: UIViewController? weak var parent: DeregisterCoordinatable? diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/PhoneNumberValidationForDeregisterVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/PhoneNumberValidationForDeregisterVM.swift index f82d6e46..0c0532b7 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/PhoneNumberValidationForDeregisterVM.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/PhoneNumberValidationForDeregisterVM.swift @@ -45,7 +45,7 @@ class PhoneNumberValidationForDeregisterVM: BaseViewModel, PhoneNumberValidation init( coordinator: PhoneNumberValidationForDeregisterCoordinator?, - deregisterReasons: [DeregisterReasonVO], + deregisterReasons: [String], inputValidationUseCase: AuthInputValidationUseCase, settingUseCase: SettingScreenUseCase ) diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/WorkerDeregisterReasonsVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/WorkerDeregisterReasonsVM.swift index 3ad080f9..0cd02ff3 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/WorkerDeregisterReasonsVM.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/WorkerDeregisterReasonsVM.swift @@ -14,7 +14,8 @@ public class WorkerDeregisterReasonsVM: BaseViewModel, DeregisterReasonVMable { public weak var coordinator: SelectReasonCoordinator? public var exitButonClicked: RxRelay.PublishRelay = .init() - public var acceptDeregisterButonClicked: PublishRelay<[DeregisterReasonVO]> = .init() + public var acceptDeregisterButonClicked: PublishRelay<[String]> = .init() + public var userType: Entity.UserType = .worker public init(coordinator: SelectReasonCoordinator) { self.coordinator = coordinator diff --git a/project/Projects/Presentation/Feature/Worker/Sources/View/Setting/WorkerSettingVC.swift b/project/Projects/Presentation/Feature/Worker/Sources/View/Setting/WorkerSettingVC.swift index 911aadb6..5dc79568 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/View/Setting/WorkerSettingVC.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/View/Setting/WorkerSettingVC.swift @@ -89,6 +89,21 @@ public class WorkerSettingVC: BaseViewController { .bind(to: viewModel.removeAccountButtonClicked) .disposed(by: disposeBag) + // 설정화면에 종속적인 뷰들입니다. + Observable + .merge( + frequentQuestionButton.rx.tap + .map { _ in SettingAdditionalInfoType.frequentQuestion }, + askButton.rx.tap + .map { _ in SettingAdditionalInfoType.contact }, + applicationPolicyButton.rx.tap + .map { _ in SettingAdditionalInfoType.termsandPolicies }, + personalDataProcessingPolicyButton.rx.tap + .map { _ in SettingAdditionalInfoType.privacyPolicy } + ) + .bind(to: viewModel.additionalInfoButtonClieck) + .disposed(by: disposeBag) + // Output viewModel .pushNotificationApproveState? @@ -185,39 +200,6 @@ public class WorkerSettingVC: BaseViewController { private func setObservable() { - // 설정화면에 종속적인 뷰들입니다. - frequentQuestionButton.rx.tap - .subscribe(onNext: { - - // 자주하는 질문뷰 - - }) - .disposed(by: disposeBag) - - askButton.rx.tap - .subscribe(onNext: { - - // 문의하기 뷰 - - }) - .disposed(by: disposeBag) - - applicationPolicyButton.rx.tap - .subscribe(onNext: { - - // 약관및 정책 - - }) - .disposed(by: disposeBag) - - personalDataProcessingPolicyButton.rx.tap - .subscribe(onNext: { - - // 개인정보 처리방침 - - }) - .disposed(by: disposeBag) - signOutButton .rx.tap .subscribe(onNext: { [weak self] _ in diff --git a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Seting/WorkerSettingVM.swift b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Seting/WorkerSettingVM.swift index e615e13d..49593ca9 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Seting/WorkerSettingVM.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Seting/WorkerSettingVM.swift @@ -25,6 +25,8 @@ public protocol WorkerSettingVMable: BaseViewModel { var signOutButtonComfirmed: PublishRelay { get } var removeAccountButtonClicked: PublishRelay { get } + var additionalInfoButtonClieck: PublishRelay { get } + // Output var pushNotificationApproveState: Driver? { get } var showSettingAlert: Driver? { get } @@ -40,14 +42,18 @@ public class WorkerSettingVM: BaseViewModel, WorkerSettingVMable { let settingUseCase: SettingScreenUseCase let centerProfileUseCase: CenterProfileUseCase - public var viewWillAppear: RxRelay.PublishRelay = .init() - public var myProfileButtonClicked: RxRelay.PublishRelay = .init() - public var approveToPushNotification: RxRelay.PublishRelay = .init() + // Input + public let viewWillAppear: PublishRelay = .init() + public let myProfileButtonClicked: PublishRelay = .init() + public let approveToPushNotification: PublishRelay = .init() + + public let signOutButtonComfirmed: PublishRelay = .init() + public let removeAccountButtonClicked: PublishRelay = .init() - public var signOutButtonComfirmed: RxRelay.PublishRelay = .init() - public var removeAccountButtonClicked: RxRelay.PublishRelay = .init() + public let additionalInfoButtonClieck: PublishRelay = .init() - public var pushNotificationApproveState: RxCocoa.Driver? + // Output + public var pushNotificationApproveState: Driver? public var showSettingAlert: Driver? public init( @@ -116,6 +122,26 @@ public class WorkerSettingVM: BaseViewModel, WorkerSettingVMable { }) .disposed(by: disposeBag) + // MARK: 추가정보 + additionalInfoButtonClieck + .subscribe(onNext: { + [weak self] type in + guard let self else { return } + + let url = type.getWebUrl() + + if !openURL(url) { + + alert.onNext( + .init( + title: "오류가 발생했습니다.", + message: "잠시후 다시시도해 주세요." + ) + ) + } + }) + .disposed(by: disposeBag) + // MARK: 로그아웃 let signOutRequestResult = signOutButtonComfirmed.flatMap({ [settingUseCase] _ in settingUseCase.signoutWorkerAccount() @@ -171,6 +197,14 @@ public class WorkerSettingVM: BaseViewModel, WorkerSettingVMable { return viewModel } + + private func openURL(_ url: URL) -> Bool { + if UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url, options: [:]) + return true + } + return false + } } class CenterSingOutVM: IdleAlertViewModelable { diff --git a/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/Deregister/DeregisterCoordinatable.swift b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/Deregister/DeregisterCoordinatable.swift index 3a5b84df..916f94d4 100644 --- a/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/Deregister/DeregisterCoordinatable.swift +++ b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/Deregister/DeregisterCoordinatable.swift @@ -17,10 +17,10 @@ public protocol DeregisterCoordinatable: ParentCoordinator { func cancelDeregister() /// 센터관리자: 마지막으로 비밀번호를 입력합니다. - func showFinalPasswordScreen(reasons: [DeregisterReasonVO]) + func showFinalPasswordScreen(reasons: [String]) /// 요양보호사: 마지막으로 전화번호를 입력합니다. - func showFinalPhoneAuthScreen(reasons: [DeregisterReasonVO]) + func showFinalPhoneAuthScreen(reasons: [String]) /// 최초화면으로 돌아갑니다. func popToRoot()