Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge :: 비밀번호 변경 Feature #140

Merged
merged 16 commits into from
Nov 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Projects/App/Sources/Application/DI/AppComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,10 @@ public extension AppComponent {
var rewardPointDetailComponent: RewardPointDetailComponent {
RewardPointDetailComponent(parent: self)
}
var checkPasswordComponent: CheckPasswordComponent {
CheckPasswordComponent(parent: self)
}
var modifyPasswordComponent: ModifyPasswordComponent {
ModifyPasswordComponent(parent: self)
}
}
48 changes: 48 additions & 0 deletions Projects/App/Sources/Application/NeedleGenerated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ private class MyPageDependency48d84b530313b3ee40feProvider: MyPageDependency {
var rewardPointDetailComponent: RewardPointDetailComponent {
return appComponent.rewardPointDetailComponent
}
var checkPasswordComponent: CheckPasswordComponent {
return appComponent.checkPasswordComponent
}
private let appComponent: AppComponent
init(appComponent: AppComponent) {
self.appComponent = appComponent
Expand All @@ -204,6 +207,19 @@ private class MyPageDependency48d84b530313b3ee40feProvider: MyPageDependency {
private func factory0f6f456ebf157d02dfb3f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
return MyPageDependency48d84b530313b3ee40feProvider(appComponent: parent1(component) as! AppComponent)
}
private class ModifyPasswordDependency8e8e0713c7893c69f342Provider: ModifyPasswordDependency {
var changePasswordUseCase: any ChangePasswordUseCase {
return appComponent.changePasswordUseCase
}
private let appComponent: AppComponent
init(appComponent: AppComponent) {
self.appComponent = appComponent
}
}
/// ^->AppComponent->ModifyPasswordComponent
private func factoryf1815b12945a9aa456a2f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
return ModifyPasswordDependency8e8e0713c7893c69f342Provider(appComponent: parent1(component) as! AppComponent)
}
private class RewardPointDetailDependency623f1251c3863ea3b233Provider: RewardPointDetailDependency {
var fetchPointListUseCase: any FetchPointListUseCase {
return appComponent.fetchPointListUseCase
Expand Down Expand Up @@ -233,6 +249,22 @@ private class ChangeProfileDependency18055275199967076a28Provider: ChangeProfile
private func factory239204ef0c47c0c68c97f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
return ChangeProfileDependency18055275199967076a28Provider(appComponent: parent1(component) as! AppComponent)
}
private class CheckPasswordDependencyd8ff624643356835c570Provider: CheckPasswordDependency {
var compareCurrentPasswordUseCase: any CompareCurrentPasswordUseCase {
return appComponent.compareCurrentPasswordUseCase
}
var modifyPasswordComponent: ModifyPasswordComponent {
return appComponent.modifyPasswordComponent
}
private let appComponent: AppComponent
init(appComponent: AppComponent) {
self.appComponent = appComponent
}
}
/// ^->AppComponent->CheckPasswordComponent
private func factorycb24ea072925f86bef40f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
return CheckPasswordDependencyd8ff624643356835c570Provider(appComponent: parent1(component) as! AppComponent)
}
private class RootDependency3944cc797a4a88956fb5Provider: RootDependency {
var signinComponent: SigninComponent {
return appComponent.signinComponent
Expand Down Expand Up @@ -423,6 +455,8 @@ extension AppComponent: Registration {
localTable["changeProfileComponent-ChangeProfileComponent"] = { self.changeProfileComponent as Any }
localTable["noticeDetailComponent-NoticeDetailComponent"] = { self.noticeDetailComponent as Any }
localTable["rewardPointDetailComponent-RewardPointDetailComponent"] = { self.rewardPointDetailComponent as Any }
localTable["checkPasswordComponent-CheckPasswordComponent"] = { self.checkPasswordComponent as Any }
localTable["modifyPasswordComponent-ModifyPasswordComponent"] = { self.modifyPasswordComponent as Any }
localTable["remoteNoticeDataSource-any RemoteNoticeDataSource"] = { self.remoteNoticeDataSource as Any }
localTable["noticeRepository-any NoticeRepository"] = { self.noticeRepository as Any }
localTable["fetchWhetherNewNoticeUseCase-any FetchWhetherNewNoticeUseCase"] = { self.fetchWhetherNewNoticeUseCase as Any }
Expand Down Expand Up @@ -521,6 +555,12 @@ extension MyPageComponent: Registration {
keyPathToName[\MyPageDependency.fetchMyProfileUseCase] = "fetchMyProfileUseCase-any FetchMyProfileUseCase"
keyPathToName[\MyPageDependency.changeProfileComponent] = "changeProfileComponent-ChangeProfileComponent"
keyPathToName[\MyPageDependency.rewardPointDetailComponent] = "rewardPointDetailComponent-RewardPointDetailComponent"
keyPathToName[\MyPageDependency.checkPasswordComponent] = "checkPasswordComponent-CheckPasswordComponent"
}
}
extension ModifyPasswordComponent: Registration {
public func registerItems() {
keyPathToName[\ModifyPasswordDependency.changePasswordUseCase] = "changePasswordUseCase-any ChangePasswordUseCase"
}
}
extension RewardPointDetailComponent: Registration {
Expand All @@ -534,6 +574,12 @@ extension ChangeProfileComponent: Registration {
keyPathToName[\ChangeProfileDependency.uploadFileUseCase] = "uploadFileUseCase-any UploadFileUseCase"
}
}
extension CheckPasswordComponent: Registration {
public func registerItems() {
keyPathToName[\CheckPasswordDependency.compareCurrentPasswordUseCase] = "compareCurrentPasswordUseCase-any CompareCurrentPasswordUseCase"
keyPathToName[\CheckPasswordDependency.modifyPasswordComponent] = "modifyPasswordComponent-ModifyPasswordComponent"
}
}
extension RootComponent: Registration {
public func registerItems() {
keyPathToName[\RootDependency.signinComponent] = "signinComponent-SigninComponent"
Expand Down Expand Up @@ -620,8 +666,10 @@ private func registerProviderFactory(_ componentPath: String, _ factory: @escapi
registerProviderFactory("^->AppComponent->SignupProfileImageComponent", factory6792674212c15df7e9cff47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->MainTabComponent", factory1ab5a747ddf21e1393f9f47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->MyPageComponent", factory0f6f456ebf157d02dfb3f47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->ModifyPasswordComponent", factoryf1815b12945a9aa456a2f47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->RewardPointDetailComponent", factory87993268d9e212be8b1af47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->ChangeProfileComponent", factory239204ef0c47c0c68c97f47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->CheckPasswordComponent", factorycb24ea072925f86bef40f47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->RootComponent", factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->SigninComponent", factory2882a056d84a613debccf47b58f8f304c97af4d5)
registerProviderFactory("^->AppComponent->HomeComponent", factory67229cdf0f755562b2b1f47b58f8f304c97af4d5)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import DomainModule
import SwiftUI
import NeedleFoundation

public protocol CheckPasswordDependency: Dependency {
var compareCurrentPasswordUseCase: any CompareCurrentPasswordUseCase { get }
var modifyPasswordComponent: ModifyPasswordComponent { get }
}

public final class CheckPasswordComponent: Component<CheckPasswordDependency> {
public func makeView() -> some View {
CheckPasswordView(
viewModel: .init(
compareCurrentPasswordUseCase: self.dependency.compareCurrentPasswordUseCase
),
modifyPasswordComponent: self.dependency.modifyPasswordComponent
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import DesignSystem
import Utility
import SwiftUI

struct CheckPasswordView: View {

@StateObject var viewModel: CheckPasswordViewModel
let modifyPasswordComponent: ModifyPasswordComponent
@Environment(\.dismiss) var dismiss

init(
viewModel: CheckPasswordViewModel,
modifyPasswordComponent: ModifyPasswordComponent
) {
_viewModel = StateObject(wrappedValue: viewModel)
self.modifyPasswordComponent = modifyPasswordComponent
}

var body: some View {
VStack(spacing: 4) {
DMSHeaderTitleView(subTitle: "기존 비밀번호")
.padding(.top, 24)

SecureDMSFloatingTextField(
"비밀번호",
text: $viewModel.password,
isError: viewModel.isPasswordRegexError,
errorMessage: "유효하지 않은 비밀번호입니다."
) {
viewModel.checkPasswordButtonDidTap()

}
.padding(.top, 56)

Spacer()

DMSWideButton(text: "다음", color: .PrimaryVariant.primary) {
viewModel.checkPasswordButtonDidTap()
}
.disabled(!viewModel.isCheckPasswordEnabled)
.padding(.bottom, 40)
}
.hideKeyboardWhenTap()
.dmsBackButton(dismiss: dismiss)
.padding(.horizontal, 24)
.dmsBackground()
.dmsToast(isShowing: $viewModel.isShowingToast, message: viewModel.errorMessage, style: .error)
.ignoresSafeArea(.keyboard, edges: .bottom)
.navigate(
to: modifyPasswordComponent.makeView(
currentPassword: viewModel.password
),
when: $viewModel.isSuccessCheckPassword
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import BaseFeature
import Combine
import DomainModule

final class CheckPasswordViewModel: BaseViewModel {
@Published var password = "" {
didSet { resettingError() }
}
@Published var isPasswordRegexError = false
@Published var isSuccessCheckPassword = false
@Published var isShowingToast = false

var isCheckPasswordEnabled: Bool {
!password.isEmpty
}

private let compareCurrentPasswordUseCase: any CompareCurrentPasswordUseCase

public init(
compareCurrentPasswordUseCase: any CompareCurrentPasswordUseCase
) {
self.compareCurrentPasswordUseCase = compareCurrentPasswordUseCase
}

func checkPasswordButtonDidTap() {
guard isCheckPasswordEnabled else {
return
}

let passwordExpression = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-]).{8,20}$"
guard password ~= passwordExpression else {
isPasswordRegexError = true
return
}

addCancellable(
compareCurrentPasswordUseCase.execute(password: password)
) { [weak self] _ in
self?.isSuccessCheckPassword = true
} onReceiveError: { [weak self] _ in
self?.isShowingToast = true
}
}

func resettingError() {
isPasswordRegexError = false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import DomainModule
import SwiftUI
import NeedleFoundation

public protocol ModifyPasswordDependency: Dependency {
var changePasswordUseCase: any ChangePasswordUseCase { get }
}

public final class ModifyPasswordComponent: Component<ModifyPasswordDependency> {
public func makeView(currentPassword: String) -> some View {
ModifyPasswordView(
viewModel: .init(
changePasswordUseCase: self.dependency.changePasswordUseCase,
currentPassword: currentPassword
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import DesignSystem
import Utility
import SwiftUI

struct ModifyPasswordView: View {
private enum FocusField {
case password
case passwordCheck
}

@FocusState private var focusField: FocusField?
@StateObject var viewModel: ModifyPasswordViewModel
@Environment(\.dismiss) var dismiss

init(
viewModel: ModifyPasswordViewModel
) {
_viewModel = StateObject(wrappedValue: viewModel)
}

var body: some View {
VStack(spacing: 4) {
DMSHeaderTitleView(subTitle: "새 비밀번호 설정")
.padding(.top, 24)

HStack {
Text("비밀번호는 영문, 숫자, 기호를 포함한 8~20자이어야 합니다.")
.dmsFont(.etc(.caption), color: .GrayScale.gray5)

Spacer()
}

VStack(spacing: 60) {
SecureDMSFloatingTextField(
"새 비밀번호 입력",
text: $viewModel.password,
isError: viewModel.isPasswordRegexError,
errorMessage: "비밀번호가 형식이 올바르지 않습니다."
) {
focusField = .passwordCheck
}
.focused($focusField, equals: .password)

SecureDMSFloatingTextField(
"새 비밀번호 확인 ",
text: $viewModel.passwordCheck,
isError: viewModel.isPasswordMismatchedError,
errorMessage: "비밀번호가 위와 일치하지 않습니다."
) {
viewModel.changePasswordButtonDidTap()
}
.focused($focusField, equals: .passwordCheck)
}
.padding(.top, 56)

Spacer()

DMSWideButton(text: "다음", color: .PrimaryVariant.primary) {
viewModel.changePasswordButtonDidTap()
}
.disabled(!viewModel.iChangePasswordEnabled)
.padding(.bottom, 40)
}
.hideKeyboardWhenTap()
.onAppear {
focusField = .password
}
.dmsBackButton(dismiss: dismiss)
.padding(.horizontal, 24)
.dmsBackground()
.dmsToast(isShowing: $viewModel.isShowingToast, message: viewModel.errorMessage, style: .error)
.ignoresSafeArea(.keyboard, edges: .bottom)
.onChange(of: viewModel.isSuccessRenewalPassword) { newValue in
if newValue {
NavigationUtil.popToRootView()
}
}
}
}
Loading