From aa66951cb459bb8de8de0546a968f191e4c489a6 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Sat, 19 Aug 2023 17:01:35 +0900 Subject: [PATCH] =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AppIcon.appiconset/Contents.json | 2 +- .../Launcher_icon_1024px.png | Bin 38135 -> 0 bytes .../launcher_icon_1024px.png | Bin 0 -> 50906 bytes .../ic_circle_x.imageset/Contents.json | 21 + .../ic_circle_x.imageset/ic_circle_x.png | Bin 0 -> 659 bytes .../ic_tag_check.imageset/Contents.json | 21 + .../ic_tag_check.imageset/ic_tag_check.png | Bin 0 -> 2316 bytes SodaLive/Sources/App/AppStep.swift | 6 + SodaLive/Sources/ContentView.swift | 9 + SodaLive/Sources/MyPage/MyInfoCardView.swift | 1 + .../MyPage/Profile/GetProfileResponse.swift | 24 + .../GetChangeNicknamePriceResponse.swift | 12 + .../Profile/Nickname/NicknameUpdateView.swift | 110 +++++ .../Nickname/NicknameUpdateViewModel.swift | 168 +++++++ .../Profile/Password/ModifyPasswordView.swift | 109 +++++ .../MyPage/Profile/ProfileUpdateRequest.swift | 24 + .../MyPage/Profile/ProfileUpdateView.swift | 430 ++++++++++++++++++ .../Profile/ProfileUpdateViewModel.swift | 329 ++++++++++++++ .../MyPage/Profile/Tag/MemberTagApi.swift | 44 ++ .../Profile/Tag/MemberTagRepository.swift | 19 + .../Profile/Tag/MemberTagResponse.swift | 14 + .../MyPage/Profile/Tag/MemberTagView.swift | 115 +++++ .../Profile/Tag/MemberTagViewModel.swift | 59 +++ SodaLive/Sources/User/UserApi.swift | 44 +- SodaLive/Sources/User/UserRepository.swift | 24 + 25 files changed, 1579 insertions(+), 6 deletions(-) delete mode 100644 SodaLive/Resources/Assets.xcassets/AppIcon.appiconset/Launcher_icon_1024px.png create mode 100644 SodaLive/Resources/Assets.xcassets/AppIcon.appiconset/launcher_icon_1024px.png create mode 100644 SodaLive/Resources/Assets.xcassets/ic_circle_x.imageset/Contents.json create mode 100644 SodaLive/Resources/Assets.xcassets/ic_circle_x.imageset/ic_circle_x.png create mode 100644 SodaLive/Resources/Assets.xcassets/ic_tag_check.imageset/Contents.json create mode 100644 SodaLive/Resources/Assets.xcassets/ic_tag_check.imageset/ic_tag_check.png create mode 100644 SodaLive/Sources/MyPage/Profile/GetProfileResponse.swift create mode 100644 SodaLive/Sources/MyPage/Profile/Nickname/GetChangeNicknamePriceResponse.swift create mode 100644 SodaLive/Sources/MyPage/Profile/Nickname/NicknameUpdateView.swift create mode 100644 SodaLive/Sources/MyPage/Profile/Nickname/NicknameUpdateViewModel.swift create mode 100644 SodaLive/Sources/MyPage/Profile/Password/ModifyPasswordView.swift create mode 100644 SodaLive/Sources/MyPage/Profile/ProfileUpdateRequest.swift create mode 100644 SodaLive/Sources/MyPage/Profile/ProfileUpdateView.swift create mode 100644 SodaLive/Sources/MyPage/Profile/ProfileUpdateViewModel.swift create mode 100644 SodaLive/Sources/MyPage/Profile/Tag/MemberTagApi.swift create mode 100644 SodaLive/Sources/MyPage/Profile/Tag/MemberTagRepository.swift create mode 100644 SodaLive/Sources/MyPage/Profile/Tag/MemberTagResponse.swift create mode 100644 SodaLive/Sources/MyPage/Profile/Tag/MemberTagView.swift create mode 100644 SodaLive/Sources/MyPage/Profile/Tag/MemberTagViewModel.swift diff --git a/SodaLive/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/SodaLive/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json index 5ca511e..98b1394 100644 --- a/SodaLive/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/SodaLive/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Launcher_icon_1024px.png", + "filename" : "launcher_icon_1024px.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/SodaLive/Resources/Assets.xcassets/AppIcon.appiconset/Launcher_icon_1024px.png b/SodaLive/Resources/Assets.xcassets/AppIcon.appiconset/Launcher_icon_1024px.png deleted file mode 100644 index d67350d67b2c143c9a3195e1968eabf5dbd0851c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38135 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 diff --git a/SodaLive/Resources/Assets.xcassets/AppIcon.appiconset/launcher_icon_1024px.png b/SodaLive/Resources/Assets.xcassets/AppIcon.appiconset/launcher_icon_1024px.png new file mode 100644 index 0000000000000000000000000000000000000000..9280e97708c6dbc995b243d96f81a2b9483919fa GIT binary patch literal 50906 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 aHhPxx3t+ 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 z≷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∈OtDK) z^t3{L$!Wa+bN?qg0Q~W>kYEt~YsNm@+z2cKLhhn06AQA(V8;b-u!~|BM?h6Cq?B2j z^tr+Tcexfc7n5&Feyl=+YQbJ>z+=yhsLqDwa+ZgR9Z}Tg+sj-B@Pq(hq5RfiKmeD} z%+?yp!YF=&hr#V2;X$0rPhxzHNQxDh?+V*s-*@hQxcFT*MQ4vcq&`x>kt-$GaBB=u zUD90)=>k+E$JW-J9U1cP!SjK?h8eKIKqM|J^Wr`kLY3dn29rZPUzMt$>xu~F@>S)~ z+go0pA~`hsqTMhF${XtfF{)Y`$W-zZBJ2ZmV=cj>m9>c#HQrWgxy1}J4yHgS%CEID zH#stTYiwQ!VL`RBr|2b+wtTbZ+D*xrd^=~lJAvVELe&r3K(IrPEF5T3@MXHFOSr>J z-W3U2t6nNuQlYll#=%opfJJgis$dVbCWeHd13$}IAh)N6@wN;2 zBET(Q2KHx1UjK=;wz3tS;$Y`s(_)q>+rTPj)wPhmQk0tj2#LTrugOuJR}C< zzu42lLzU9*r5?ei(L6`>z0ln4O2E#*@luWt?(5U~w>A^iH>RbzH+K-=#)z0ss;-0C)8X~G%ffz^y==T(v4a6febjNfzLqs??x`q%$#tQSw z{;=i4fK7RqT#`R_CVHu5XG>^`87l%N$z0h*^Ls;HJi3tr4^|p?V_dD-KK-5FSuL7qQ+S;dGHYX_n8j#8)n9~Rs9RH;p!D;o1k24n40udWCTBrQN-%i z=$OR50B-kH_AWC)?IcJbn5$zC5j?rO{J6Rb?aSI>vA2NBUJ`l9M~PgTV774rqRk8~ zzcHS-%q0>u`Z`k)0DnIO`)Eqh72gAO3o?GDG?+xW z{q}!0GNMop-XHKff0oUnVsu^UDLf$T45biQn0!=9m5!B&BK z+zxFk9w3OnC4h>qubHDeSeSP%Frp3$ULIlLln{&9caBo0r&0`vMo=Zz(!RBu>=Z7e zu#G?*9VmZhwI@h@ZHcwInC{JXD!Iw)&)vPdw`w0r45$5w(dOOhc4&19TFPu7N6kL2 zujC5?34mY(ZgzAt*FK`;DF%+dRGCd~*LtJ&y>oSTN3}R`c=-TNPPX>_q?X8CZY|31 zdF|(vy#`5YQ=^s4h6U9ZK^O(gh@K=<4!jD$E%CpZI`aSXZs=0`eNS%G}U;vjb? zhIQv;UIIC>0X`wF?gc*o$pteD{=a?p|K#TX7x(|4SN?xt_5Wvg{_k1%|Mr>xtGoWs zulWD^r%fKvBH@xCzhH(5Hzl6ieB}OdCUZpvi)zHyeSV)mS2_dTwAItaF~sBe)M>Y; zH7oGA1_nB;vEt2Ie(UG|^D?Wq6?uxBkB+?CJwax&ftc5msbPBmI_;#c?-J8DLVfBiF6;lc{bSJzJvY7m7@t#h33-R-|T5PlApFcBd zv{?G>wpT}J_3dE2#hM=b`n?`Dy)xLBIp@&-(%SsvJ1XHQIm+imB z&nBFde`3#@B>nJ6z4vmA+Z^6qtX(Gc`@zd(?JK+6=ByOsGvHy;obe&+X@bTVKEv<* zIz0`~yuuxlL`>2HnOi3ccr0r;Bqzdh;KIL6Nh&NnZ&T!Z8X6uRzdPS>hPu7TugtZb z3k<)+#&;Zw@N)e7*)eGzudMrph#Pa)JPL|@kSw=uw_r_`N2{-SbYN>qRdvk8D{@#7td`uU_mdIHDU-hGLlW6vA{>Z}C zEW5`GcQLH2t$Uf#5xqHbg~y5lwc=y?c3INX10^}lrv9o)IK>vZ=T`S^w<~3fSNvVQ zY`wR1)#YT%IHtIJecw;*=CfL&<$8p#+W*RQpVH1Z3;!QhUhbW>x>f=hzYLzPelF{r G5}E+z_)bg! literal 0 HcmV?d00001 diff --git a/SodaLive/Resources/Assets.xcassets/ic_tag_check.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_tag_check.imageset/Contents.json new file mode 100644 index 0000000..17d60f9 --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/ic_tag_check.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "ic_tag_check.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/ic_tag_check.imageset/ic_tag_check.png b/SodaLive/Resources/Assets.xcassets/ic_tag_check.imageset/ic_tag_check.png new file mode 100644 index 0000000000000000000000000000000000000000..0243889f83c94e36256fa093f7fb3a512e4f0cad GIT binary patch literal 2316 zcmV+n3G?=eP)Px#giuUWMgRZ*j1~We3jW>Q-SzeL?Ck92<>g0kO#J-( z`S|+P)Yb9v@w##Uk|F-e$;-aJzOk{fmMH(Eq^0xa|Hq8~pF01pS^vFe|C=@crb_?T ztN-iN|DrF#Inw2m>5QZnX+6@RQ1q21uR$XnE_kVvoKnUR^5CSIi zx2=7j@teuyIPjlH3vNm5pU;kMBkl3fkZn8XvmYn7KWTdEW7Oid@5R zqto%|dDbCp|K0eqDD>WGB4FB+0Uc(XFnbf-*)Im&Kt=~If&mtJ4R9{(@zB?5w$KIw zGvL)wu&=X2CspLo!-~+yK;z#wys(O$%nE(0AlsG;oaS29PrA(p9r&y)tP@PKlf}x! z59WbR5R2@>WOG?;l*|BqV^RjP3=m?5#W=#biH43DWKR%shV3!fu~6uX!w5qMp{6q; zaW3MZ_oX)uE(kaGP=1ShQP8C{&=F>_BN`$)Re@}s?AsD)_fVttYaC{2wFC~pK9%(JY2^|!(R_zFj z*|CVBbHD=~6sb--!Gd-wsGWcVy6`iKmShG!D#GzR9=g!8?ZU$5+I_l)p1ct*&del{ zcpP+*pF;y)U`F^nU$;XiFJ)$^li|=syA!}$C z^eYnwllV}(tZUFWSp;2`_3bINBaNUdvUZ+9ClU#|BI~3ZIzSSPDy?G-3Ob>E$;813 z#P5P8QHXSzpaC^wk%hLU${e)}Vz)z+C_I2uBxDA!R-;0707r(5&<3gw=1_IC1yY(-bv0BRtr>Dc8(PKH1o|Si zqp7Pe73f&f99_%RaRnO5?dW1d2C>j23{TQR!-)+=U}{nuBsQC=P&4R2lUyA%6t#dt zllsn;8QNuPJt%i7LXg_Ra!(_L4kRu+8U;u|ps!MohP6@gN}&DaW-K~2-WiNfpzWp9 zfmw~*c(6TMRRfiDvlg8Wo>Q1K5ckJ!au8xYL8c2nQ zmSHY7>uv1o@vcUs(gU)ie3+XVtJ%DlC)s{h9Iif)y=NeCXw959u$#?0a~?L8^?EF{ zMIsT%l-8Ry1T*Fz_UQ4ixq4s!842x?=`E0%)@kOnQDc7mY*OfvqCSy17m)GLSe6-W zd7uq4Cc^JP3e%Vf9&!UbDa`{<*Xv7$ND zL2}-i6Iw5dw!G48E7_hhlTn-(+Tq&KDvCDq=e~L;C7V8k`7>wE#W8yFlJ9gmWaZ|_ z#cZb0to^Q5O~3Uqzx^?EUTB*@j~oyQGpT>;A3B*i98HEqo8;?gw4*T*bz(-% zW-!Ve3S}l0H3E%eMNHQ*(#k9cxlj6ySC}_7=8uWYWNl0Xp3);_!WBKs!?5U-*?z*z zw_uZh5-)Vhq_tBQ(-iT{6q-vODYUuytT5d(9t?yBops~KkAEK|KTlw$^56=@9J*Rj zP@1xbO_) zIMy=Nwo#|n!o1yXR+E|OFwRvl<@&&`{-{%LOcRS;t1<6(LYSvjdD-Zaxw=WctuudN zX^3H_&|H&%qF`!Lqus8Bd1DG;rjr1^>6T7nv4#3;K4v=I;+)u$nyH2QF&{IX*m5o* z(6z3o)S6gkx`e>HSVLF4o=}Tsri(S)%VPc1+F!BE<+2$60%RYxaAsx!l7FeUO0CV# z%q;azSX8e@qt-FC62{Cds!!Rz=%${7nc2RWv{BSWJqt6lQ8aN&u9cdZc8gL!ZEU9Cv{93^sN+C%zaCEzSLrIMc;?!1AQ1Ok^Hj`}*q&pghXy*D6H!+6^C!Y2hwj#zHCKPmRQ!h5Wbm7P? z$2Nr>kezUVtB_PtQs5$e%m*kv}D$Q&jWeN;RJdRr4{{S3Y|iaX)+evImA?$^*k{KIN?BquFXc zPp;@g@M=D}zhrX*5Z2s4%{CGXHSEyEHtbNdHHxB5Xy$GA12eVzQM0j Void, onClickCancel: () -> Void ) + + case modifyPassword + + case changeNickname + + case profileUpdate(refresh: () -> Void) } diff --git a/SodaLive/Sources/ContentView.swift b/SodaLive/Sources/ContentView.swift index 5480aae..17ac89d 100644 --- a/SodaLive/Sources/ContentView.swift +++ b/SodaLive/Sources/ContentView.swift @@ -133,6 +133,15 @@ struct ContentView: View { onClickCancel: onClickCancel ) + case .modifyPassword: + ModifyPasswordView() + + case .changeNickname: + NicknameUpdateView() + + case .profileUpdate(let refresh): + ProfileUpdateView(refresh: refresh) + default: EmptyView() .frame(width: 0, height: 0, alignment: .topLeading) diff --git a/SodaLive/Sources/MyPage/MyInfoCardView.swift b/SodaLive/Sources/MyPage/MyInfoCardView.swift index 0bdfbfe..3287d31 100644 --- a/SodaLive/Sources/MyPage/MyInfoCardView.swift +++ b/SodaLive/Sources/MyPage/MyInfoCardView.swift @@ -31,6 +31,7 @@ struct MyInfoCardView: View { Button(action: { if AppState.shared.roomId <= 0 { + AppState.shared.setAppStep(step: .profileUpdate(refresh: refresh)) } }) { Image("ic_myinfo_edit") diff --git a/SodaLive/Sources/MyPage/Profile/GetProfileResponse.swift b/SodaLive/Sources/MyPage/Profile/GetProfileResponse.swift new file mode 100644 index 0000000..bde8e04 --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/GetProfileResponse.swift @@ -0,0 +1,24 @@ +// +// GetProfileResponse.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import Foundation + +struct GetProfileResponse: Decodable { + let userId: Int + let email: String + let nickname: String + let gender: Gender + var profileUrl: String + let chargeCan: Int + let rewardCan: Int + let youtubeUrl: String? + let instagramUrl: String? + let blogUrl: String? + let websiteUrl: String? + let introduce: String + let tags: [String] +} diff --git a/SodaLive/Sources/MyPage/Profile/Nickname/GetChangeNicknamePriceResponse.swift b/SodaLive/Sources/MyPage/Profile/Nickname/GetChangeNicknamePriceResponse.swift new file mode 100644 index 0000000..6697e9b --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/Nickname/GetChangeNicknamePriceResponse.swift @@ -0,0 +1,12 @@ +// +// GetChangeNicknamePriceResponse.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import Foundation + +struct GetChangeNicknamePriceResponse: Decodable { + let price: Int +} diff --git a/SodaLive/Sources/MyPage/Profile/Nickname/NicknameUpdateView.swift b/SodaLive/Sources/MyPage/Profile/Nickname/NicknameUpdateView.swift new file mode 100644 index 0000000..b0b06c1 --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/Nickname/NicknameUpdateView.swift @@ -0,0 +1,110 @@ +// +// NicknameUpdateView.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import SwiftUI + +struct NicknameUpdateView: View { + + @StateObject var viewModel = NicknameUpdateViewModel() + @StateObject var keyboardHandler = KeyboardHandler() + + var body: some View { + BaseView(isLoading: $viewModel.isLoading) { + GeometryReader { proxy in + VStack(spacing: 0) { + DetailNavigationBar(title: "프로필 수정") + + Text("닉네임 변경으로 인해 피해를 입는 사용자가 지속적으로 발생하여 닉네임 변경을 부득이하게 유료로 전환합니다.") + .fixedSize(horizontal: false, vertical: true) + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + .frame(width: screenSize().width - 40, alignment: .leading) + .padding(.top, 40) + + Text("최초 1회에 한해서 무료로 변경이 가능하고, 그 이후부터는 유료로 전환됩니다.") + .fixedSize(horizontal: false, vertical: true) + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "dd4500")) + .frame(width: screenSize().width - 40, alignment: .leading) + + UserTextField( + title: "닉네임(최대 12자)", + hint: "닉네임", + isSecure: false, + variable: $viewModel.nickname + ) + .frame(width: screenSize().width - 40) + .padding(.top, 40) + + Text("중복확인") + .font(.custom(Font.bold.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + .frame(width: screenSize().width - 40) + .padding(.vertical, 13.3) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color(hex: "9970ff"), lineWidth: 1) + ) + .padding(.top, 21.3) + .onTapGesture { + hideKeyboard() + viewModel.checkNickname() + } + + Spacer() + + Text(viewModel.price > 0 ? "\(viewModel.price)코인으로 닉네임 변경하기" : "닉네임 변경하기") + .font(.custom(Font.bold.rawValue, size: 18.3)) + .foregroundColor(Color.white) + .padding(.vertical, 16) + .frame(width: screenSize().width - 26.7) + .background(Color(hex: "9970ff")) + .cornerRadius(10) + .padding(.vertical, 13.7) + .frame(width: screenSize().width) + .background(Color(hex: "222222")) + .cornerRadius(16.7) + .padding(.top, 13.3) + .onTapGesture { viewModel.changeNickname() } + + if proxy.safeAreaInsets.bottom > 0 { + Rectangle() + .foregroundColor(Color.black) + .frame(width: proxy.size.width, height: 15.3) + } + } + .edgesIgnoringSafeArea(.bottom) + .onTapGesture { + hideKeyboard() + } + } + .onAppear { + viewModel.nickname = UserDefaults.string(forKey: .nickname) + viewModel.getChangeNicknamePrice() + } + .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { + GeometryReader { geo in + HStack { + Spacer() + Text(viewModel.errorMessage) + .padding(.vertical, 13.3) + .padding(.horizontal, 6.7) + .frame(width: geo.size.width - 66.7, alignment: .center) + .font(.custom(Font.medium.rawValue, size: 12)) + .background(Color(hex: "9970ff")) + .foregroundColor(Color.white) + .multilineTextAlignment(.leading) + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(20) + .padding(.top, 66.7) + Spacer() + } + } + } + } + } +} diff --git a/SodaLive/Sources/MyPage/Profile/Nickname/NicknameUpdateViewModel.swift b/SodaLive/Sources/MyPage/Profile/Nickname/NicknameUpdateViewModel.swift new file mode 100644 index 0000000..864b3d9 --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/Nickname/NicknameUpdateViewModel.swift @@ -0,0 +1,168 @@ +// +// NicknameUpdateViewModel.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import Foundation +import Combine + +import Moya + +final class NicknameUpdateViewModel: ObservableObject { + private let repository = UserRepository() + private var subscription = Set() + + @Published var isLoading = false + @Published var errorMessage = "" + @Published var isShowPopup = false + + @Published var nickname = "" { + didSet { + if nickname.count > 12 { + nickname = String(nickname.prefix(12)) + } + + isCheckedNickname = false + } + } + @Published var price = 0 + + var isCheckedNickname = false + + func getChangeNicknamePrice() { + isLoading = true + + repository.getChangeNicknamePrice() + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) + + if let data = decoded.data, decoded.success { + self.price = data.price + } else { + if let message = decoded.message { + self.errorMessage = message + } else { + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + } + + self.isShowPopup = true + } + } catch { + ERROR_LOG(error.localizedDescription) + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.isShowPopup = true + } + } + .store(in: &subscription) + } + + func checkNickname() { + if !nickname.trimmingCharacters(in: .whitespaces).isEmpty { + isLoading = true + + repository.checkNickname(nickname: nickname) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] 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.isCheckedNickname = true + self.errorMessage = "사용가능한 닉네임 입니다." + self.isShowPopup = true + } 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) + } else { + self.errorMessage = "닉네임을 입력하세요." + self.isShowPopup = true + } + } + + func changeNickname() { + if isCheckedNickname { + isLoading = true + + let request = ProfileUpdateRequest(email: UserDefaults.string(forKey: .email), nickname: nickname) + repository.changeNickname(request: request) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] 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 { + UserDefaults.set(nickname, forKey: .nickname) + self.errorMessage = "닉네임이 변경되었습니다." + 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) + + } else { + self.errorMessage = "닉네임 중복체크를 해주세요." + self.isShowPopup = true + } + } +} diff --git a/SodaLive/Sources/MyPage/Profile/Password/ModifyPasswordView.swift b/SodaLive/Sources/MyPage/Profile/Password/ModifyPasswordView.swift new file mode 100644 index 0000000..b7c586a --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/Password/ModifyPasswordView.swift @@ -0,0 +1,109 @@ +// +// ModifyPasswordView.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import SwiftUI + +struct ModifyPasswordView: View { + + @StateObject var viewModel = ProfileUpdateViewModel() + @StateObject var keyboardHandler = KeyboardHandler() + + var body: some View { + BaseView(isLoading: $viewModel.isLoading) { + GeometryReader { proxy in + VStack(spacing: 0) { + DetailNavigationBar(title: "비밀번호 변경") + + ScrollView(.vertical, showsIndicators: false) { + Text("안전한 비밀번호로 내 내 정보를 보호하세요") + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + .padding(.top, 40) + + VStack(spacing: 26.7) { + UserTextField( + title: "현재 비밀번호", + hint: "현재 비밀번호를 입력하세요.", + isSecure: true, + variable: $viewModel.currentPassword + ) + + UserTextField( + title: "신규 비밀번호", + hint: "신규 비밀번호를 입력해주세요(영문, 숫자 포함 8자 이상)", + isSecure: true, + variable: $viewModel.newPassword + ) + + UserTextField( + title: "신규 비밀번호 확인", + hint: "신규 비밀번호를 재입력해주세요", + isSecure: true, + variable: $viewModel.newPasswordConfirm + ) + } + .padding(.top, 40) + .frame(width: screenSize().width - 53.4) + + Text("* 영문, 숫자 포함 8자 이상") + .font(.custom(Font.medium.rawValue, size: 12)) + .foregroundColor(Color(hex: "dd4500")) + .frame(width: screenSize().width - 53.4, alignment: .leading) + .padding(.top, 13.7) + } + + if !viewModel.isLoading { + Text("비밀번호 변경하기") + .font(.custom(Font.bold.rawValue, size: 18.3)) + .foregroundColor(.white) + .padding(.vertical, 16) + .frame(width: screenSize().width - 26.7) + .background(Color(hex: "9970ff")) + .cornerRadius(10) + .padding(.vertical, 13.7) + .frame(width: screenSize().width) + .background(Color(hex: "222222")) + .cornerRadius(16.7, corners: [.topLeft, .topRight]) + .onTapGesture { + hideKeyboard() + viewModel.updatePassword() + } + + if proxy.safeAreaInsets.bottom > 0 { + Rectangle() + .foregroundColor(Color(hex: "222222")) + .frame(width: proxy.size.width, height: 15.3) + } + } + } + .edgesIgnoringSafeArea(.bottom) + .onTapGesture { + hideKeyboard() + } + } + } + .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { + GeometryReader { geo in + HStack { + Spacer() + Text(viewModel.errorMessage) + .padding(.vertical, 13.3) + .padding(.horizontal, 6.7) + .frame(width: geo.size.width - 66.7, alignment: .center) + .font(.custom(Font.medium.rawValue, size: 12)) + .background(Color(hex: "9970ff")) + .foregroundColor(Color.white) + .multilineTextAlignment(.leading) + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(20) + .padding(.top, 66.7) + Spacer() + } + } + } + } +} diff --git a/SodaLive/Sources/MyPage/Profile/ProfileUpdateRequest.swift b/SodaLive/Sources/MyPage/Profile/ProfileUpdateRequest.swift new file mode 100644 index 0000000..d21d2e4 --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/ProfileUpdateRequest.swift @@ -0,0 +1,24 @@ +// +// ProfileUpdateRequest.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import Foundation + +struct ProfileUpdateRequest: Encodable { + let email: String + var password: String? = nil + var modifyPassword: String? = nil + var nickname: String? = nil + var gender: Gender? = nil + var introduce: String? = nil + var youtubeUrl: String? = nil + var instagramUrl: String? = nil + var websiteUrl: String? = nil + var blogUrl: String? = nil + let container: String = "ios" + var insertTags: [String]? = nil + var removeTags: [String]? = nil +} diff --git a/SodaLive/Sources/MyPage/Profile/ProfileUpdateView.swift b/SodaLive/Sources/MyPage/Profile/ProfileUpdateView.swift new file mode 100644 index 0000000..5fced00 --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/ProfileUpdateView.swift @@ -0,0 +1,430 @@ +// +// ProfileUpdateView.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import SwiftUI +import Kingfisher + +struct ProfileUpdateView: View { + @StateObject var viewModel = ProfileUpdateViewModel() + @StateObject var keyboardHandler = KeyboardHandler() + + @State private var isShowPhotoPicker = false + @State private var isShowSelectTagView = false + + let refresh: () -> Void + + @ViewBuilder + func EmailAndPasswordView() -> some View { + VStack(spacing: 26.7) { + VStack(alignment: .leading, spacing: 0) { + Text("이메일") + .font(.custom(Font.medium.rawValue, size: 12)) + .foregroundColor(Color(hex: "eeeeee")) + .padding(.leading, 6.7) + + Text(viewModel.email) + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "777777")) + .padding(.top, 12) + .padding(.leading, 6.7) + + Divider() + .frame(height: 0.3) + .foregroundColor(Color(hex: "909090")) + .padding(.top, 8.3) + } + + HStack(alignment: .bottom, spacing: 13.3) { + VStack(alignment: .leading, spacing: 0) { + Text("비밀번호") + .font(.custom(Font.medium.rawValue, size: 12)) + .foregroundColor(Color(hex: "eeeeee")) + .padding(.leading, 6.7) + + Text("********") + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "777777")) + .padding(.top, 12) + .padding(.leading, 6.7) + + Divider() + .frame(height: 0.3) + .foregroundColor(Color(hex: "909090")) + .padding(.top, 8.3) + } + + Button(action: { AppState.shared.setAppStep(step: .modifyPassword) }) { + Text("비밀번호 변경") + .font(.custom(Font.bold.rawValue, size: 13.3)) + .foregroundColor(Color.white) + .padding(.vertical, 13.3) + .padding(.horizontal, 22.7) + .background(Color(hex: "9970ff")) + .cornerRadius(8) + } + } + } + .padding(.vertical, 20) + .padding(.horizontal, 13.3) + .frame(width: screenSize().width - 26.7) + .background(Color(hex: "222222")) + .cornerRadius(6.7) + } + + @ViewBuilder + func NicknameAndGenderView() -> some View { + VStack(spacing: 16.7) { + HStack(alignment: .bottom, spacing: 13.3) { + VStack(alignment: .leading, spacing: 0) { + Text("닉네임") + .font(.custom(Font.medium.rawValue, size: 12)) + .foregroundColor(Color(hex: "eeeeee")) + .padding(.leading, 6.7) + + Text(viewModel.nickname) + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + .padding(.top, 12) + .padding(.leading, 6.7) + + Divider() + .frame(height: 0.3) + .foregroundColor(Color(hex: "909090")) + .padding(.top, 8.3) + } + + Button(action: { AppState.shared.setAppStep(step: .changeNickname) }) { + Text("닉네임 변경") + .font(.custom(Font.bold.rawValue, size: 13.3)) + .foregroundColor(Color.white) + .padding(.vertical, 13.3) + .padding(.horizontal, 22.7) + .background(Color(hex: "9970ff")) + .cornerRadius(8) + } + } + + VStack(alignment: .leading, spacing: 13.3) { + Text("성별") + .font(.custom(Font.bold.rawValue, size: 12)) + .foregroundColor(Color(hex: "eeeeee")) + .padding(.leading, 6.7) + + HStack(spacing: 0) { + Button(action: { viewModel.gender = .FEMALE }) { + HStack(spacing: 13.3) { + Image(viewModel.gender == .FEMALE ? "btn_radio_select_selected" : "btn_radio_select_normal") + .resizable() + .frame(width: 20, height: 20) + + Text("여자") + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + } + } + + Spacer() + + Button(action: { viewModel.gender = .MALE }) { + HStack(spacing: 13.3) { + Image(viewModel.gender == .MALE ? "btn_radio_select_selected" : "btn_radio_select_normal") + .resizable() + .frame(width: 20, height: 20) + + Text("남자") + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + } + } + + Spacer() + + Button(action: { viewModel.gender = .NONE }) { + HStack(spacing: 13.3) { + Image(viewModel.gender == .NONE ? "btn_radio_select_selected" : "btn_radio_select_normal") + .resizable() + .frame(width: 20, height: 20) + + Text("공개 안 함") + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + } + } + + Spacer() + } + .padding(.horizontal, 6.7) + } + } + .padding(.vertical, 20) + .padding(.horizontal, 13.3) + .frame(width: screenSize().width - 26.7) + .background(Color(hex: "222222")) + .cornerRadius(6.7) + } + + @ViewBuilder + func InstagramAndYoutubeAccountView() -> some View { + VStack(spacing: 16.7) { + UserTextField( + title: "인스타그램", + hint: "인스타그램 URL", + isSecure: false, + variable: $viewModel.instagramUrl + ) + + UserTextField( + title: "유튜브", + hint: "유튜브 URL", + isSecure: false, + variable: $viewModel.youtubeUrl + ) + + UserTextField( + title: "웹사이트", + hint: "웹사이트 URL", + isSecure: false, + variable: $viewModel.websiteUrl + ) + + UserTextField( + title: "블로그", + hint: "블로그 URL", + isSecure: false, + variable: $viewModel.blogUrl + ) + } + .padding(.vertical, 20) + .padding(.horizontal, 13.3) + .frame(width: screenSize().width - 26.7) + .background(Color(hex: "222222")) + .cornerRadius(6.7) + } + + @ViewBuilder + func TagSelectView() -> some View { + VStack(alignment: .leading, spacing: 13.3) { + Text("관심사") + .font(.custom(Font.bold.rawValue, size: 16.7)) + .foregroundColor(Color(hex: "eeeeee")) + + Button(action: { + hideKeyboard() + isShowSelectTagView = true + }) { + Text("관심사 선택") + .font(.custom(Font.bold.rawValue, size: 16.7)) + .foregroundColor(Color(hex: "9970ff")) + .padding(.vertical, 13.7) + .frame(width: screenSize().width - 53.4) + .background(Color(hex: "9970ff").opacity(0.2)) + .cornerRadius(24.3) + .overlay( + RoundedRectangle(cornerRadius: 24.3) + .stroke() + .foregroundColor(Color(hex: "9970ff")) + ) + } + + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 10) { + ForEach(viewModel.tags, id: \.self) { tag in + HStack(spacing: 6.7) { + Text(tag) + .font(.custom(Font.medium.rawValue, size: 14.7)) + .foregroundColor(.white) + + Image("ic_circle_x") + .onTapGesture { + if let index = viewModel.tags.firstIndex(of: tag) { + viewModel.tags.remove(at: index) + } + } + } + .padding(10) + .background(Color(hex: "9970ff")) + .cornerRadius(24.3) + } + } + } + .padding(.top, 13.3) + } + .padding(.vertical, 20) + .padding(.horizontal, 13.3) + .frame(width: screenSize().width - 26.7) + .background(Color(hex: "222222")) + .cornerRadius(6.7) + } + + @ViewBuilder + func ContentInputView() -> some View { + VStack(alignment: .leading, spacing: 13.3) { + Text("소개글") + .font(.custom(Font.bold.rawValue, size: 16.7)) + .foregroundColor(Color(hex: "eeeeee")) + + TextViewWrapper( + text: $viewModel.introduce, + placeholder: viewModel.placeholder, + textColorHex: "eeeeee", + backgroundColorHex: "222222" + ) + .frame(width: screenSize().width - 26.7, height: 133.3) + .padding(.top, 13.3) + } + .frame(width: screenSize().width - 26.7) + } + + var body: some View { + BaseView(isLoading: $viewModel.isLoading) { + GeometryReader { proxy in + ZStack { + VStack(spacing: 0) { + DetailNavigationBar(title: "프로필 수정") + + ScrollView(.vertical, showsIndicators: false) { + VStack(spacing: 0) { + if let profileResponse = viewModel.profileResponse { + ZStack { + if profileResponse.profileUrl.trimmingCharacters(in: .whitespaces).count > 0 { + KFImage(URL(string: profileResponse.profileUrl)) + .resizable() + .scaledToFill() + .frame(width: 80, height: 80, alignment: .top) + .clipShape(Circle()) + } else { + Image("ic_logo") + .resizable() + .scaledToFill() + .frame(width: 80, height: 80, alignment: .top) + .background(Color(hex: "3e3358")) + .clipShape(Circle()) + } + + Image("ic_camera") + .padding(10) + .background(Color(hex: "9970ff")) + .cornerRadius(30) + .offset(x: 25, y: 25) + } + .frame(alignment: .bottomTrailing) + .padding(.top, 13.3) + .onTapGesture { + isShowPhotoPicker = true + } + + EmailAndPasswordView() + .padding(.top, 26.7) + + NicknameAndGenderView() + .padding(.top, 13.3) + + InstagramAndYoutubeAccountView() + .padding(.top, 13.3) + + TagSelectView() + .padding(.top, 33.3) + + ContentInputView() + .padding(.top, 33.3) + + Text("저장하기") + .font(.custom(Font.bold.rawValue, size: 18.3)) + .foregroundColor(Color.white) + .frame(width: screenSize().width - 26.7, height: 50) + .background(Color(hex: "9970ff")) + .cornerRadius(10) + .padding(.vertical, 13.7) + .padding(.horizontal, 13.3) + .frame(width: screenSize().width) + .background(Color(hex: "222222")) + .cornerRadius(16.7, corners: [.topLeft, .topRight]) + .padding(.top, 26.7) + .onTapGesture { + viewModel.updateProfile() + } + + if proxy.safeAreaInsets.bottom > 0 { + Rectangle() + .foregroundColor(Color(hex: "222222")) + .frame(width: proxy.size.width, height: 15.3) + } + } + } + } + .padding(.top, 13.3) + } + + if isShowPhotoPicker { + ImagePicker( + isShowing: $isShowPhotoPicker, + selectedImage: $viewModel.profileImage, + sourceType: .photoLibrary + ) + } + + if isShowSelectTagView { + GeometryReader { proxy in + VStack { + Spacer() + MemberTagView( + isShowing: $isShowSelectTagView, + selectedTags: $viewModel.tags, + onItemClick: { tag, isChecked in + if isChecked { + viewModel.addTag(tag: tag) + } else { + viewModel.removeTag(tag: tag) + } + } + ) + .frame(width: proxy.size.width, height: proxy.size.height * 0.9) + .offset(y: isShowSelectTagView ? 0 : proxy.size.height * 0.9) + .animation(.easeInOut(duration: 0.49), value: self.isShowSelectTagView) + } + } + .edgesIgnoringSafeArea(.bottom) + } + } + .offset(y: keyboardHandler.keyboardHeight > 0 ? -keyboardHandler.keyboardHeight + 15.3 : 0) + .edgesIgnoringSafeArea(.bottom) + .onTapGesture { + hideKeyboard() + } + } + .onAppear { + viewModel.refresh = refresh + viewModel.getMyProfile() + } + .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { + GeometryReader { geo in + HStack { + Spacer() + Text(viewModel.errorMessage) + .padding(.vertical, 13.3) + .padding(.horizontal, 6.7) + .frame(width: geo.size.width - 66.7, alignment: .center) + .font(.custom(Font.medium.rawValue, size: 12)) + .background(Color(hex: "9970ff")) + .foregroundColor(Color.white) + .multilineTextAlignment(.leading) + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(20) + .padding(.top, 66.7) + Spacer() + } + } + } + } + } +} + +struct ProfileUpdateView_Previews: PreviewProvider { + static var previews: some View { + ProfileUpdateView(refresh: {}) + } +} diff --git a/SodaLive/Sources/MyPage/Profile/ProfileUpdateViewModel.swift b/SodaLive/Sources/MyPage/Profile/ProfileUpdateViewModel.swift new file mode 100644 index 0000000..da7f7e8 --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/ProfileUpdateViewModel.swift @@ -0,0 +1,329 @@ +// +// ProfileUpdateViewModel.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import UIKit +import Combine + +import Moya + +final class ProfileUpdateViewModel: ObservableObject { + + private let repository = UserRepository() + private var subscription = Set() + + @Published var isLoading = false + @Published var errorMessage = "" + @Published var isShowPopup = false + + @Published var email = "" + @Published var nickname = "" + @Published var youtubeUrl = "" + @Published var instagramUrl = "" + @Published var websiteUrl = "" + @Published var blogUrl = "" + @Published var gender: Gender = .NONE + @Published var introduce = "" + + @Published var currentPassword = "" + @Published var newPassword = "" + @Published var newPasswordConfirm = "" + + @Published var tags = [String]() + @Published var insertTags = [String]() + @Published var removeTags = [String]() + + var profileImage: UIImage? = nil { + didSet { + if let _ = profileImage { + updateProfileImage() + } + } + } + + var profileResponse: GetProfileResponse? + + var refresh: () -> Void = {} + + let placeholder = "소개글을 입력하세요" + + func getMyProfile() { + isLoading = true + repository.getMyProfile() + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) + + if let data = decoded.data, decoded.success { + self.profileResponse = data + + self.email = data.email + self.nickname = data.nickname + self.youtubeUrl = data.youtubeUrl ?? "" + self.instagramUrl = data.instagramUrl ?? "" + self.blogUrl = data.blogUrl ?? "" + self.websiteUrl = data.websiteUrl ?? "" + self.introduce = data.introduce + self.gender = data.gender + self.tags.append(contentsOf: data.tags) + + UserDefaults.set(data.nickname, forKey: .nickname) + } 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) + } + + func updateProfile() { + if profileResponse!.nickname != nickname || + profileResponse!.youtubeUrl != youtubeUrl || + profileResponse!.instagramUrl != instagramUrl || + profileResponse!.blogUrl != blogUrl || + profileResponse!.websiteUrl != websiteUrl || + profileResponse!.gender != gender || + profileResponse!.introduce != introduce || + !insertTags.isEmpty || + !removeTags.isEmpty { + + let request = ProfileUpdateRequest( + email: profileResponse!.email, + nickname: profileResponse!.nickname != nickname ? nickname : nil, + gender: profileResponse!.gender != gender ? gender : nil, + introduce: profileResponse!.introduce != introduce && introduce.trimmingCharacters(in: .whitespacesAndNewlines) != placeholder ? introduce : nil, + youtubeUrl: profileResponse!.youtubeUrl != youtubeUrl ? youtubeUrl : nil, + instagramUrl: profileResponse!.instagramUrl != instagramUrl ? instagramUrl : nil, + websiteUrl: profileResponse!.websiteUrl != websiteUrl ? websiteUrl : nil, + blogUrl: profileResponse!.blogUrl != blogUrl ? blogUrl : nil, + insertTags: !insertTags.isEmpty ? insertTags : nil, + removeTags: !removeTags.isEmpty ? removeTags : nil + ) + + isLoading = true + repository.profileUpdate(request: request) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) + + if let _ = decoded.data, decoded.success { + self.refresh() + self.errorMessage = "프로필이 변경되었습니다." + 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) + + } else { + AppState.shared.back() + } + } + + func updatePassword() { + if currentPassword.trimmingCharacters(in: .whitespaces).isEmpty { + errorMessage = "현재 비밀번호를 입력하세요." + isShowPopup = true + return + } + + if newPassword.trimmingCharacters(in: .whitespaces).isEmpty { + errorMessage = "변경할 비밀번호를 입력하세요." + isShowPopup = true + return + } + + if newPassword != newPasswordConfirm { + errorMessage = "비밀번호가 일치하지 않습니다." + isShowPopup = true + return + } + + if !validatePassword() { + errorMessage = "영문, 숫자 포함 8자 이상의 비밀번호를 입력해 주세요." + isShowPopup = true + return + } + + let request = ProfileUpdateRequest( + email: UserDefaults.string(forKey: .email), + password: currentPassword, + modifyPassword: newPassword + ) + + isLoading = true + repository.profileUpdate(request: request) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) + + if let _ = decoded.data, decoded.success { + self.errorMessage = "비밀번호가 변경되었습니다." + self.isShowPopup = true + self.currentPassword = "" + self.newPassword = "" + self.newPasswordConfirm = "" + + 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) + } + + func removeTag(tag: String) { + if let index = tags.firstIndex(of: tag) { + tags.remove(at: index) + } + + if (insertTags.contains(tag)) { + if let index = insertTags.firstIndex(of: tag) { + insertTags.remove(at: index) + } + } else { + removeTags.append(tag) + } + } + + func addTag(tag: String) { + tags.append(tag) + if (removeTags.contains(tag)) { + if let index = removeTags.firstIndex(of: tag) { + removeTags.remove(at: index) + } + } else { + insertTags.append(tag) + } + } + + private func updateProfileImage() { + isLoading = false + + if let profileImage = profileImage, let imageData = profileImage.jpegData(compressionQuality: 0.8) { + repository + .profileImageUpdate(parameter: MultipartFormData( + provider: .data(imageData), + name: "image", + fileName: "\(UUID().uuidString)_\(Date().timeIntervalSince1970 * 1000).jpg", + mimeType: "image/*" + )) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) + + if let _ = decoded.data, decoded.success { + self.refresh() + self.getMyProfile() + } 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) + } else { + self.errorMessage = "프로필 이미지를 업데이트 하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.isShowPopup = true + self.isLoading = false + } + } + + private func validatePassword() -> Bool { + let passwordRegEx = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d$@!%*#?&]{8,}$" + + let predicate = NSPredicate(format:"SELF MATCHES %@", passwordRegEx) + return predicate.evaluate(with: newPassword) + } +} diff --git a/SodaLive/Sources/MyPage/Profile/Tag/MemberTagApi.swift b/SodaLive/Sources/MyPage/Profile/Tag/MemberTagApi.swift new file mode 100644 index 0000000..51d5025 --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/Tag/MemberTagApi.swift @@ -0,0 +1,44 @@ +// +// MemberTagApi.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import Foundation +import Moya + +enum MemberTagApi { + case getTags +} + +extension MemberTagApi: TargetType { + var baseURL: URL { + return URL(string: BASE_URL)! + } + + var path: String { + switch self { + case .getTags: + return "/member/tag" + } + } + + var method: Moya.Method { + switch self { + case .getTags: + return .get + } + } + + var task: Task { + switch self { + case .getTags: + return .requestPlain + } + } + + var headers: [String : String]? { + return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"] + } +} diff --git a/SodaLive/Sources/MyPage/Profile/Tag/MemberTagRepository.swift b/SodaLive/Sources/MyPage/Profile/Tag/MemberTagRepository.swift new file mode 100644 index 0000000..d3ac1b5 --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/Tag/MemberTagRepository.swift @@ -0,0 +1,19 @@ +// +// MemberTagRepository.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import Foundation +import CombineMoya +import Combine +import Moya + +final class MemberTagRepository { + private let api = MoyaProvider() + + func getTags() -> AnyPublisher { + return api.requestPublisher(.getTags) + } +} diff --git a/SodaLive/Sources/MyPage/Profile/Tag/MemberTagResponse.swift b/SodaLive/Sources/MyPage/Profile/Tag/MemberTagResponse.swift new file mode 100644 index 0000000..1d23d7a --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/Tag/MemberTagResponse.swift @@ -0,0 +1,14 @@ +// +// MemberTagResponse.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import Foundation + +struct MemberTagResponse: Decodable, Hashable { + let id: Int + let tag: String + let image: String +} diff --git a/SodaLive/Sources/MyPage/Profile/Tag/MemberTagView.swift b/SodaLive/Sources/MyPage/Profile/Tag/MemberTagView.swift new file mode 100644 index 0000000..70794d5 --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/Tag/MemberTagView.swift @@ -0,0 +1,115 @@ +// +// MemberTagView.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import SwiftUI +import Kingfisher + +struct MemberTagView: View { + + let columns = [ + GridItem(.flexible()), + GridItem(.flexible()), + GridItem(.flexible()), + GridItem(.flexible()) + ] + + @StateObject var viewModel = MemberTagViewModel() + + @Binding var isShowing: Bool + @Binding var selectedTags: [String] + let onItemClick: (String, Bool) -> Void + + var body: some View { + ZStack { + Color(hex: "222222").ignoresSafeArea() + + VStack(spacing: 0) { + HStack(alignment: .top, spacing: 0) { + Text("관심사 선택") + .font(.custom(Font.bold.rawValue, size: 18.3)) + .foregroundColor(.white) + + Spacer() + + Image("ic_close_white") + .resizable() + .frame(width: 20, height: 20) + .onTapGesture { + isShowing = false + } + } + .padding(.horizontal, 26.7) + .padding(.top, 26.7) + + ScrollView(.vertical, showsIndicators: false) { + LazyVGrid(columns: columns, spacing: 26.7) { + ForEach(viewModel.tags, id: \.self) { tag in + VStack(spacing: 16.7) { + ZStack { + KFImage(URL(string: tag.image)) + .resizable() + .scaledToFill() + .frame(width: 60, height: 60, alignment: .top) + .clipped() + + if selectedTags.contains(tag.tag) { + Image("ic_tag_check") + .resizable() + .frame(width: 60, height: 60) + } + } + + Text(tag.tag) + .font(.custom(Font.medium.rawValue, size: 14.7)) + .foregroundColor( + selectedTags.contains(tag.tag) ? + Color(hex: "9970ff") : + Color(hex: "bbbbbb") + ) + } + .onTapGesture { + onItemClick(tag.tag, !selectedTags.contains(tag.tag)) + } + } + } + } + .padding(.horizontal, 20) + .padding(.top, 26.7) + + Text("확인") + .font(.custom(Font.bold.rawValue, size: 18.3)) + .foregroundColor(.white) + .padding(.vertical, 16) + .frame(width: screenSize().width - 26.7) + .background(Color(hex: "9970ff")) + .cornerRadius(10) + .padding(.bottom, 26.7) + .onTapGesture { + isShowing = false + } + } + + if viewModel.isLoading { + LoadingView() + } + } + .cornerRadius(16.7, corners: [.topLeft, .topRight]) + .onAppear { + viewModel.getTags() + } + } +} + +struct MemberTagView_Previews: PreviewProvider { + static var previews: some View { + MemberTagView( + isShowing: .constant(true), + selectedTags: .constant(["여행"]), + onItemClick: { _, _ in } + ) + } +} diff --git a/SodaLive/Sources/MyPage/Profile/Tag/MemberTagViewModel.swift b/SodaLive/Sources/MyPage/Profile/Tag/MemberTagViewModel.swift new file mode 100644 index 0000000..3af589a --- /dev/null +++ b/SodaLive/Sources/MyPage/Profile/Tag/MemberTagViewModel.swift @@ -0,0 +1,59 @@ +// +// MemberTagViewModel.swift +// SodaLive +// +// Created by klaus on 2023/08/19. +// + +import Foundation +import Combine + +final class MemberTagViewModel: ObservableObject { + + private let repository = MemberTagRepository() + private var subscription = Set() + + @Published var isLoading = false + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var tags: [MemberTagResponse] = [] + + func getTags() { + isLoading = true + + repository.getTags() + .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(ApiResponse<[MemberTagResponse]>.self, from: responseData) + + if let data = decoded.data, decoded.success { + self.tags.removeAll() + self.tags.append(contentsOf: data) + } 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/UserApi.swift b/SodaLive/Sources/User/UserApi.swift index 670af70..252540c 100644 --- a/SodaLive/Sources/User/UserApi.swift +++ b/SodaLive/Sources/User/UserApi.swift @@ -24,6 +24,12 @@ enum UserApi { case creatorUnFollow(request: CreatorFollowRequest) case memberBlock(request: MemberBlockRequest) case memberUnBlock(request: MemberBlockRequest) + case getMyProfile + case profileImageUpdate(parameter: MultipartFormData) + case profileUpdate(request: ProfileUpdateRequest) + case getChangeNicknamePrice + case checkNickname(nickname: String) + case changeNickname(request: ProfileUpdateRequest) } extension UserApi: TargetType { @@ -77,18 +83,34 @@ extension UserApi: TargetType { case .memberUnBlock: return "/member/unblock" + + case .getMyProfile, .profileUpdate: + return "/member" + + case .profileImageUpdate: + return "/member/image" + + case .getChangeNicknamePrice: + return "/member/change/nickname/price" + + case .checkNickname: + return "/member/check/nickname" + + case .changeNickname: + return "/member/change/nickname" } } var method: Moya.Method { switch self { - case .login, .signUp, .findPassword, .notification, .logout, .logoutAllDevice, .signOut, .creatorFollow, .creatorUnFollow, .memberBlock, .memberUnBlock: + case .login, .signUp, .findPassword, .notification, .logout, .logoutAllDevice, .signOut, .creatorFollow, .creatorUnFollow, .memberBlock, .memberUnBlock, + .profileImageUpdate: return .post - case .searchUser, .getMypage, .getMemberInfo: + case .searchUser, .getMypage, .getMemberInfo, .getMyProfile, .getChangeNicknamePrice, .checkNickname: return .get - case .updatePushToken: + case .updatePushToken, .profileUpdate, .changeNickname: return .put } } @@ -107,10 +129,10 @@ extension UserApi: TargetType { case .searchUser(let nickname): return .requestParameters(parameters: ["nickname" : nickname], encoding: URLEncoding.queryString) - case .getMypage: + case .getMypage, .getMyProfile: return .requestParameters(parameters: ["container" : "ios"], encoding: URLEncoding.queryString) - case .getMemberInfo, .logout, .logoutAllDevice: + case .getMemberInfo, .logout, .logoutAllDevice, .getChangeNicknamePrice: return .requestPlain case .notification(let request): @@ -133,6 +155,18 @@ extension UserApi: TargetType { case .memberUnBlock(let request): return .requestJSONEncodable(request) + + case .profileImageUpdate(let parameter): + return .uploadMultipart([parameter]) + + case .profileUpdate(let request): + return .requestJSONEncodable(request) + + case .checkNickname(let nickname): + return .requestParameters(parameters: ["nickname" : nickname], encoding: URLEncoding.queryString) + + case .changeNickname(let request): + return .requestJSONEncodable(request) } } diff --git a/SodaLive/Sources/User/UserRepository.swift b/SodaLive/Sources/User/UserRepository.swift index 7c2d8b0..a5855ff 100644 --- a/SodaLive/Sources/User/UserRepository.swift +++ b/SodaLive/Sources/User/UserRepository.swift @@ -84,4 +84,28 @@ final class UserRepository { func memberUnBlock(userId: Int) -> AnyPublisher { return api.requestPublisher(.memberUnBlock(request: MemberBlockRequest(blockMemberId: userId))) } + + func getMyProfile() -> AnyPublisher { + return api.requestPublisher(.getMyProfile) + } + + func profileImageUpdate(parameter: MultipartFormData) -> AnyPublisher { + return api.requestPublisher(.profileImageUpdate(parameter: parameter)) + } + + func profileUpdate(request: ProfileUpdateRequest) -> AnyPublisher { + return api.requestPublisher(.profileUpdate(request: request)) + } + + func getChangeNicknamePrice() -> AnyPublisher { + return api.requestPublisher(.getChangeNicknamePrice) + } + + func checkNickname(nickname: String) -> AnyPublisher { + return api.requestPublisher(.checkNickname(nickname: nickname)) + } + + func changeNickname(request: ProfileUpdateRequest) -> AnyPublisher { + return api.requestPublisher(.changeNickname(request: request)) + } }