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 13 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
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()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import BaseFeature
import Combine
import DomainModule

final class ModifyPasswordViewModel: BaseViewModel {
@Published var password = "" {
didSet { resettingError() }
}
@Published var passwordCheck = "" {
didSet { resettingError() }
}
@Published var isPasswordRegexError = false
@Published var isPasswordMismatchedError = false
@Published var isSuccessRenewalPassword = false
@Published var isShowingToast = false

var iChangePasswordEnabled: Bool {
!password.isEmpty && !passwordCheck.isEmpty
}

let currentPassword: String
private let changePasswordUseCase: any ChangePasswordUseCase

public init(
changePasswordUseCase: any ChangePasswordUseCase,
currentPassword: String
) {
self.changePasswordUseCase = changePasswordUseCase
self.currentPassword = currentPassword
}
func changePasswordButtonDidTap() {
guard iChangePasswordEnabled else {
return
}

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

guard password == passwordCheck else {
isPasswordMismatchedError = true
return
}
addCancellable(
changePasswordUseCase.execute(
req: .init(
currentPassword: currentPassword,
newPassword: password
)
)
) { [weak self] _ in
self?.isSuccessRenewalPassword = true
} onReceiveError: { [weak self] _ in
self?.isShowingToast = true
}
}

func resettingError() {
isPasswordRegexError = false
isPasswordMismatchedError = false
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public protocol MyPageDependency: Dependency {
var fetchMyProfileUseCase: any FetchMyProfileUseCase { get }
var changeProfileComponent: ChangeProfileComponent { get }
var rewardPointDetailComponent: RewardPointDetailComponent { get }
var checkPasswordComponent: CheckPasswordComponent { get }
}

public final class MyPageComponent: Component<MyPageDependency> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ struct MyPageView: View {

private let changeProfileComponent: ChangeProfileComponent
private let rewardPointDetailComponent: RewardPointDetailComponent
private let checkPasswordComponent: CheckPasswordComponent

init(
viewModel: MyPageViewModel,
changeProfileComponent: ChangeProfileComponent,
rewardPointDetailComponent: RewardPointDetailComponent
rewardPointDetailComponent: RewardPointDetailComponent,
checkPasswordComponent: CheckPasswordComponent
) {
_viewModel = StateObject(wrappedValue: viewModel)
self.changeProfileComponent = changeProfileComponent
self.rewardPointDetailComponent = rewardPointDetailComponent
self.checkPasswordComponent = checkPasswordComponent
}

var body: some View {
Expand Down Expand Up @@ -89,9 +92,16 @@ struct MyPageView: View {
Divider()
.padding(.horizontal, 10)

myPageOptionRowCardView(title: "비밀번호 변경")
.dmsFont(.body(.body2), color: .GrayScale.gray6)
.cornerRadius(10, corners: [.bottomLeft, .bottomRight])
NavigationLink {
DeferView {
checkPasswordComponent.makeView()
}
} label: {
myPageOptionRowCardView(title: "비밀번호 변경")
.dmsFont(.body(.body2), color: .GrayScale.gray6)
.cornerRadius(10, corners: [.bottomLeft, .bottomRight])
}

}
.background {
RoundedRectangle(cornerRadius: 10)
Expand Down