From 10aebcc9816436c8c2aa3325d6cce8ea1870b9dc Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Wed, 9 Aug 2023 21:03:42 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=B0=BE=EA=B8=B0=20=ED=8E=98=EC=9D=B4=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ic_back.imageset/Contents.json | 21 ++++ .../ic_back.imageset/ic_back.png | Bin 0 -> 274 bytes .../Contents.json | 21 ++++ .../ic_headphones_purple.png | Bin 0 -> 1316 bytes .../Common/ApiResponseWithoutData.swift | 14 +++ SodaLive/Sources/ContentView.swift | 3 + .../User/FindPassword/FindPasswordView.swift | 104 ++++++++++++++++++ .../FindPassword/FindPasswordViewModel.swift | 69 ++++++++++++ .../FindPassword/ForgotPasswordRequest.swift | 12 ++ SodaLive/Sources/User/UserApi.swift | 11 +- SodaLive/Sources/User/UserRepository.swift | 4 + 11 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 SodaLive/Resources/Assets.xcassets/ic_back.imageset/Contents.json create mode 100644 SodaLive/Resources/Assets.xcassets/ic_back.imageset/ic_back.png create mode 100644 SodaLive/Resources/Assets.xcassets/ic_headphones_purple.imageset/Contents.json create mode 100644 SodaLive/Resources/Assets.xcassets/ic_headphones_purple.imageset/ic_headphones_purple.png create mode 100644 SodaLive/Sources/Common/ApiResponseWithoutData.swift create mode 100644 SodaLive/Sources/User/FindPassword/FindPasswordView.swift create mode 100644 SodaLive/Sources/User/FindPassword/FindPasswordViewModel.swift create mode 100644 SodaLive/Sources/User/FindPassword/ForgotPasswordRequest.swift diff --git a/SodaLive/Resources/Assets.xcassets/ic_back.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_back.imageset/Contents.json new file mode 100644 index 0000000..c07489e --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/ic_back.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "ic_back.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/ic_back.imageset/ic_back.png b/SodaLive/Resources/Assets.xcassets/ic_back.imageset/ic_back.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d3679c182a375cd74ede4902fd4aa5fe85de91 GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKx3?xrnI^qbVSkfJRf%L|H?mvmFKt5w}kh>GZ zx^prwfgF_ppAc8~0-yKq-+%h_>Cc}(K*qaw?||fo4rgWSDyieOFdm2Lo7}wCoIt4AP{&!`LxbnYmWXc58vIr{W0s_Ue@)` z?pgyGZyUltPYXP&9AnH}cUI1KwdtOeeinU`*V=y`W_<0sA?PeVUur+3L+wOb{- z`5?GP)K~#7F?VC+< z(=Zgr|8_gTj+_9@2?!@3aDf6BfG&VxSTs2S?gD ztJIm)ezIj-c2nZtOxlTL%hKOZPtubDbaZrdtT6#v;^R>D{s03N;5rm;guvD1`~`4C zwK-G@2lO?0HVzWBK%)Y@8L9z2;2wRp=xc;>By`_&4(yQ_Q#6Ek0r*WH13UCahxD}v zKb(rO>)SHC3D8i(Gf;&mK85fi!26Nfp}rfV!G@wkeyikw@C z+h+7hl=*McqCkrmp@fJ&tf8_1B7lstA>Z;yi+D!?MszX1vI0{KGEs3CWrjDzneNofHb8qba0 zeL4=t@Cb?Vs6dEHB9s!qnEf|c8c$|ErO$R#lNj^-m#}w@8Cl(}GS7Dwps3F;v@?p) z0#ujSSc61zi9~Sib|Kt}VQ>6)Ta9PTvqqNeh({(k;v|6Se-AC$BdX8@EyF&Bg_rVp zSbjHDLx*CDyFi}lw`uk^a=OEey26d3uHa$;F=W&KeBO;`xDLEd5VCr|^t3#eTmj76 zmrS3>_z_lwx$X*?H%)cl;n`~5y+sxfi>WjZZ)?5of~m`{kX2{}VL|3?Avy7}2=R@X zcqQp{wt~F-tO9T_15)1)ELO1sEcALKP0zGNB+mjKi4RfuiWP;|9T0p$-l8hZ$;g;L zjjvx;sqd%ZA&(lHSqqquoQNoZ@tjenpul~GbzaQnZV=f<15p6-oP8-h&=K2JXDTKB z4n&783!r2HW>wbPZDYD<)(=o9Ub;TCBt}AVf{R0vPEeMGoXrAyLRzJ$&Up~Xv%;&= zwa&}o@B&!?Il`Z=*OrL~{Fkh~0GlPQjW{rpP7HN0GHH8+cu&FExn;QC0In;z;VgjC z;pu@T4-|n(D1UOSH!~A7!1DGRJL=G0S7&^7fru)`(V2Y{&-V(4<7s>S+X8;K;F|CO zC!uqnB5+xoZo5#WY03%qHsEjF5Ek!3)hW9fcsl)dq_$8K<9H)9gtHUmomlepnJ_dv zCJsAEk=an4grmNo2BBpsO9H>TO#VQM`jTjqoG|_w$_#@drUJ1PK(#EY$}&hN@XMeO zm{Z)p2%lYGDx~@7x0y0%{Pz}>>ZZb2b2=Qr}4AsnR^>@pqAGp zeW5Pmrp~72p`W3Tqn&|+5fqBzjmG7MMYf}|w3RkNO?cWXM4~tv^aFbOoh+sI7Zyuh z=r(2R2n*EE{gP*Z)6_b`dW!j>#UW~}D{L!S#_9viv6BSVpX%iliCJy#qB@83ocpZ; zaOEigc^?%-;L~G1Pm!1-aomV9@lNKD+VCLnMdXeG&neW+W^6fmA#rN 프로필 설정에서\n비밀번호를 변경하고 이용하세요.") + .font(.custom(Font.medium.rawValue, size: 12)) + .foregroundColor(Color(hex: "909090")) + .multilineTextAlignment(.center) + .lineSpacing(6) + .padding(.top, 40) + .padding(.horizontal, 26.7) + + UserTextField( + title: "이메일", + hint: "이메일 주소를 입력해 주세요", + isSecure: false, + variable: $viewModel.email, + keyboardType: .emailAddress + ) + .padding(.top, 40) + .padding(.horizontal, 26.7) + + Text("임시 비밀번호 받기") + .font(.custom(Font.bold.rawValue, size: 18.3)) + .foregroundColor(Color.white) + .frame(maxWidth: proxy.size.width - 26.7) + .padding(.vertical, 16) + .background(Color(hex: "9970ff")) + .cornerRadius(6.7) + .padding(.top, 60) + .onTapGesture { viewModel.findPassword() } + + HStack(spacing: 13.3) { + Image("ic_headphones_purple") + + Text("고객센터로 문의하기") + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "9970ff")) + } + .padding(.vertical, 10.7) + .padding(.horizontal, 18.7) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color(hex: "9970ff"), lineWidth: 1) + ) + .padding(.top, 93) + .onTapGesture { + UIApplication.shared.open(URL(string: "http://pf.kakao.com/_sZaeb")!) + } + } + } + } + } + } + .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .bottom, autohideIn: 2) { + HStack { + Spacer() + Text(viewModel.errorMessage) + .padding(.vertical, 13.3) + .padding(.horizontal, 13.3) + .frame(width: screenSize().width - 66.7, alignment: .center) + .font(.custom(Font.medium.rawValue, size: 12)) + .background(Color(hex: "9970ff")) + .foregroundColor(Color.white) + .multilineTextAlignment(.leading) + .cornerRadius(20) + .padding(.bottom, 66.7) + Spacer() + } + } + } +} + +struct FindPasswordView_Previews: PreviewProvider { + static var previews: some View { + FindPasswordView() + } +} diff --git a/SodaLive/Sources/User/FindPassword/FindPasswordViewModel.swift b/SodaLive/Sources/User/FindPassword/FindPasswordViewModel.swift new file mode 100644 index 0000000..7fe761e --- /dev/null +++ b/SodaLive/Sources/User/FindPassword/FindPasswordViewModel.swift @@ -0,0 +1,69 @@ +// +// FindPasswordViewModel.swift +// SodaLive +// +// Created by klaus on 2023/08/09. +// + +import Foundation +import Combine + +import Moya + +final class FindPasswordViewModel: ObservableObject { + private let repository = UserRepository() + private var subscription = Set() + + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false + + @Published var email = "" + + func findPassword() { + if email.trimmingCharacters(in: .whitespaces).isEmpty { + errorMessage = "이메일을 입력하세요." + isShowPopup = true + return + } + + isLoading = true + repository.findPassword(email: email) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { response in + self.isLoading = false + let responseData = response.data + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData) + + if decoded.success { + self.email = "" + self.errorMessage = "임시 비밀번호가 입력하신 이메일로 발송되었습니다.\n이메일을 확인해 주세요." + self.isShowPopup = true + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + AppState.shared.back() + } + } else { + if let message = decoded.message { + self.errorMessage = message + } else { + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + } + + self.isShowPopup = true + } + } catch { + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.isShowPopup = true + } + } + .store(in: &subscription) + } +} diff --git a/SodaLive/Sources/User/FindPassword/ForgotPasswordRequest.swift b/SodaLive/Sources/User/FindPassword/ForgotPasswordRequest.swift new file mode 100644 index 0000000..04bf2fe --- /dev/null +++ b/SodaLive/Sources/User/FindPassword/ForgotPasswordRequest.swift @@ -0,0 +1,12 @@ +// +// ForgotPasswordRequest.swift +// SodaLive +// +// Created by klaus on 2023/08/09. +// + +import Foundation + +struct ForgotPasswordRequest: Encodable { + let email: String +} diff --git a/SodaLive/Sources/User/UserApi.swift b/SodaLive/Sources/User/UserApi.swift index a55cfa0..e96dfcd 100644 --- a/SodaLive/Sources/User/UserApi.swift +++ b/SodaLive/Sources/User/UserApi.swift @@ -11,6 +11,7 @@ import Moya enum UserApi { case login(request: LoginRequest) case signUp(parameters: [MultipartFormData]) + case findPassword(request: ForgotPasswordRequest) } extension UserApi: TargetType { @@ -25,12 +26,15 @@ extension UserApi: TargetType { case .signUp: return "/member/signup" + + case .findPassword: + return "/forgot-password" } } var method: Moya.Method { switch self { - case .login, .signUp: + case .login, .signUp, .findPassword: return .post } } @@ -42,12 +46,15 @@ extension UserApi: TargetType { case .signUp(let parameters): return .uploadMultipart(parameters) + + case .findPassword(let request): + return .requestJSONEncodable(request) } } var headers: [String : String]? { switch self { - case .login, .signUp: + case .login, .signUp, .findPassword: return nil default: diff --git a/SodaLive/Sources/User/UserRepository.swift b/SodaLive/Sources/User/UserRepository.swift index fe73663..010d402 100644 --- a/SodaLive/Sources/User/UserRepository.swift +++ b/SodaLive/Sources/User/UserRepository.swift @@ -20,4 +20,8 @@ final class UserRepository { func signUp(parameters: [MultipartFormData]) -> AnyPublisher { return api.requestPublisher(.signUp(parameters: parameters)) } + + func findPassword(email: String) -> AnyPublisher { + return api.requestPublisher(.findPassword(request: ForgotPasswordRequest(email: email))) + } }