From 3387a1b369eb1f52b339d8096b1b3bad577fc0f4 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Wed, 3 Jan 2024 17:43:20 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BF=A0=ED=8F=B0=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=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 --- .../ic_coupon.imageset/Contents.json | 21 +++ .../ic_coupon.imageset/ic_coupon.png | Bin 0 -> 873 bytes SodaLive/Sources/App/AppStep.swift | 2 + SodaLive/Sources/ContentView.swift | 3 + SodaLive/Sources/MyPage/Can/CanApi.swift | 10 +- .../Sources/MyPage/Can/CanRepository.swift | 5 + .../Coupon/CanChargeCouponButtonView.swift | 34 +++++ .../Can/Coupon/CanCouponNoticeItemView.swift | 34 +++++ .../MyPage/Can/Coupon/CanCouponView.swift | 132 ++++++++++++++++++ .../Can/Coupon/CanCouponViewModel.swift | 65 +++++++++ .../Can/Coupon/UseCanCouponRequest.swift | 10 ++ SodaLive/Sources/MyPage/CanCardView.swift | 4 +- SodaLive/Sources/MyPage/MyPageView.swift | 9 ++ .../MyPage/ReservationStatusView.swift | 4 +- 14 files changed, 328 insertions(+), 5 deletions(-) create mode 100644 SodaLive/Resources/Assets.xcassets/ic_coupon.imageset/Contents.json create mode 100644 SodaLive/Resources/Assets.xcassets/ic_coupon.imageset/ic_coupon.png create mode 100644 SodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swift create mode 100644 SodaLive/Sources/MyPage/Can/Coupon/CanCouponNoticeItemView.swift create mode 100644 SodaLive/Sources/MyPage/Can/Coupon/CanCouponView.swift create mode 100644 SodaLive/Sources/MyPage/Can/Coupon/CanCouponViewModel.swift create mode 100644 SodaLive/Sources/MyPage/Can/Coupon/UseCanCouponRequest.swift diff --git a/SodaLive/Resources/Assets.xcassets/ic_coupon.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_coupon.imageset/Contents.json new file mode 100644 index 0000000..d7744fd --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/ic_coupon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "ic_coupon.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/ic_coupon.imageset/ic_coupon.png b/SodaLive/Resources/Assets.xcassets/ic_coupon.imageset/ic_coupon.png new file mode 100644 index 0000000000000000000000000000000000000000..0ced12802fee19c512d608eb2def4955b56f044e GIT binary patch literal 873 zcmV-v1D5=WP)$;K;SKqK#K5>>fs(e>9N>YyJB zYZ57t1JG5PLfUU3*4dg@kT6F0$pSldLK!pJ&=$d{lu@NS5OQKR1 z-;{5$kGg{+QPegJ_r+NE3rlf)VIDr3LF8cS6^>DrMAS-qOL1IbGJGjYe>9m%iFKqz z&D$@;NibjwbMjn)s5vii2&V7K73NoZN|i(jYea>=)hRtw`29NA!b~(!LJ}?HyJ`)K z(s)Xl1sb6)0$;YjsAopHPQ5iXE9XwT;(ritiL)9xYd8*VqI{wcVOn%(m8)#9g*+Mwqc50_Si= z^J^-~a}>OS!C3ohNk5J6e$@Udue!OgWfP)GC=wRHhE0kpa00TsqP9UDO73veaGRc~ zBzcowXW1F+7+dCpgN>+oCyr|G$dtTE#;i@%ygF^5wP6jwfiJ3_kuTh2>ESv48V`Vnb__1V8zm8M6h0iBs=5#cN9Suk93B%t zown>6v-Df5(CIf%+~Ew4&ZHg%3jfp;S6=5>+00000NkvXXu0mjf8>fNz literal 0 HcmV?d00001 diff --git a/SodaLive/Sources/App/AppStep.swift b/SodaLive/Sources/App/AppStep.swift index 78e0f70..f685933 100644 --- a/SodaLive/Sources/App/AppStep.swift +++ b/SodaLive/Sources/App/AppStep.swift @@ -117,4 +117,6 @@ enum AppStep { case creatorCommunityWrite(onSuccess: () -> Void) case creatorCommunityModify(postId: Int, onSuccess: () -> Void) + + case canCoupon } diff --git a/SodaLive/Sources/ContentView.swift b/SodaLive/Sources/ContentView.swift index 04910ed..9e2d228 100644 --- a/SodaLive/Sources/ContentView.swift +++ b/SodaLive/Sources/ContentView.swift @@ -172,6 +172,9 @@ struct ContentView: View { case .creatorCommunityModify(let postId, let onSuccess): CreatorCommunityModifyView(postId: postId, onSuccess: onSuccess) + case .canCoupon: + CanCouponView() + default: EmptyView() .frame(width: 0, height: 0, alignment: .topLeading) diff --git a/SodaLive/Sources/MyPage/Can/CanApi.swift b/SodaLive/Sources/MyPage/Can/CanApi.swift index 68cf356..27072cb 100644 --- a/SodaLive/Sources/MyPage/Can/CanApi.swift +++ b/SodaLive/Sources/MyPage/Can/CanApi.swift @@ -19,6 +19,8 @@ enum CanApi { case pgChargeCan(request: PgChargeRequest) case pgVerify(request: PgVerifyRequest) + + case useCanCoupon(request: UseCanCouponRequest) } extension CanApi: TargetType { @@ -51,6 +53,9 @@ extension CanApi: TargetType { case .pgVerify: return "/charge/verify" + + case .useCanCoupon: + return "/can/coupon/use" } } @@ -59,7 +64,7 @@ extension CanApi: TargetType { case .getCanStatus, .getCanChargeStatus, .getCanUseStatus, .getCans: return .get - case .chargeCan, .verify, .pgChargeCan, .pgVerify: + case .chargeCan, .verify, .pgChargeCan, .pgVerify, .useCanCoupon: return .post } } @@ -97,6 +102,9 @@ extension CanApi: TargetType { case .pgVerify(let request): return .requestJSONEncodable(request) + + case .useCanCoupon(let request): + return .requestJSONEncodable(request) } } diff --git a/SodaLive/Sources/MyPage/Can/CanRepository.swift b/SodaLive/Sources/MyPage/Can/CanRepository.swift index b5e4f08..2c34207 100644 --- a/SodaLive/Sources/MyPage/Can/CanRepository.swift +++ b/SodaLive/Sources/MyPage/Can/CanRepository.swift @@ -44,5 +44,10 @@ final class CanRepository { func getCans() -> AnyPublisher { return api.requestPublisher(.getCans) } + + func useCanCoupon(couponNumber: String) -> AnyPublisher { + let request = UseCanCouponRequest(couponNumber: couponNumber) + return api.requestPublisher(.useCanCoupon(request: request)) + } } diff --git a/SodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swift b/SodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swift new file mode 100644 index 0000000..e2b5d2b --- /dev/null +++ b/SodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swift @@ -0,0 +1,34 @@ +// +// CanChargeCouponButtonView.swift +// SodaLive +// +// Created by klaus on 2024/01/03. +// + +import SwiftUI + +struct CanChargeCouponButtonView: View { + var body: some View { + HStack(spacing: 5.3) { + Text("쿠폰 등록") + .font(.custom(Font.bold.rawValue, size: 16)) + .foregroundColor(Color(hex: "eeeeee")) + + Image("ic_coupon") + + Spacer() + + Image("ic_forward") + } + .padding(.horizontal, 13.3) + .padding(.vertical, 20) + .background(Color(hex: "13181b")) + .cornerRadius(6.7) + } +} + +struct CanChargeCouponButtonView_Previews: PreviewProvider { + static var previews: some View { + CanChargeCouponButtonView() + } +} diff --git a/SodaLive/Sources/MyPage/Can/Coupon/CanCouponNoticeItemView.swift b/SodaLive/Sources/MyPage/Can/Coupon/CanCouponNoticeItemView.swift new file mode 100644 index 0000000..f0b99af --- /dev/null +++ b/SodaLive/Sources/MyPage/Can/Coupon/CanCouponNoticeItemView.swift @@ -0,0 +1,34 @@ +// +// CanCouponNoticeItemView.swift +// SodaLive +// +// Created by klaus on 2024/01/03. +// + +import SwiftUI + +struct CanCouponNoticeItemView: View { + + let notice: String + + var body: some View { + HStack(alignment: .top, spacing: 4) { + Text("-") + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + + Text(notice) + .multilineTextAlignment(.leading) + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + .fixedSize(horizontal: false, vertical: true) + } + .padding(.horizontal, 10) + } +} + +struct CanCouponNoticeItemView_Previews: PreviewProvider { + static var previews: some View { + CanCouponNoticeItemView(notice: "본인인증한 계정 1회에 한해 이벤트 쿠폰등록이 적용 됩니다.") + } +} diff --git a/SodaLive/Sources/MyPage/Can/Coupon/CanCouponView.swift b/SodaLive/Sources/MyPage/Can/Coupon/CanCouponView.swift new file mode 100644 index 0000000..a1b527b --- /dev/null +++ b/SodaLive/Sources/MyPage/Can/Coupon/CanCouponView.swift @@ -0,0 +1,132 @@ +// +// CanCouponView.swift +// SodaLive +// +// Created by klaus on 2024/01/03. +// + +import SwiftUI + +struct CanCouponView: View { + + @StateObject var viewModel = CanCouponViewModel() + + var body: some View { + BaseView(isLoading: $viewModel.isLoading) { + GeometryReader { proxy in + VStack(spacing: 0) { + DetailNavigationBar(title: "쿠폰등록") + + ScrollView(.vertical, showsIndicators: false) { + VStack(spacing: 0) { + Text("쿠폰번호 입력") + .font(.custom(Font.bold.rawValue, size: 16.7)) + .foregroundColor(Color(hex: "eeeeee")) + .frame(maxWidth: .infinity, alignment: .leading) + + TextField("쿠폰번호를 입력하세요", text: $viewModel.couponNumber) + .autocapitalization(.allCharacters) // Force uppercase keyboard + .textContentType(.none) + .disableAutocorrection(true) + .font(.custom(Font.medium.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + .padding(.vertical, 16.7) + .padding(.horizontal, 13.3) + .background(Color(hex: "222222")) + .cornerRadius(6.7) + .keyboardType(.default) + .padding(.top, 13.3) + .onChange(of: viewModel.couponNumber) { newString in + let filtered = newString.filter { $0.isUppercase || $0.isNumber } + if filtered != newString { + viewModel.couponNumber = filtered + } + } + + Text("등록하기") + .font(.custom(Font.bold.rawValue, size: 18.3)) + .foregroundColor(.white) + .padding(.vertical, 16) + .frame(maxWidth: .infinity) + .background(Color(hex: "3bb9f1")) + .cornerRadius(10) + .padding(.top, 21.3) + .onTapGesture { + viewModel.useCoupon() + } + + VStack(alignment: .leading, spacing: 0) { + Text("등록안내") + .font(.custom(Font.bold.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + + CanCouponNoticeItemView(notice: "공백없이 쿠폰번호 16자리를 입력해주세요.") + .padding(.top, 8) + + CanCouponNoticeItemView(notice: "입력한 쿠폰은 소멸시까지 취소처리가 되지 않습니다.") + .padding(.top, 4) + + CanCouponNoticeItemView(notice: "충전된 캔은 캔 충전현황에서 확인할 수 있습니다.") + .padding(.top, 4) + + Rectangle() + .foregroundColor(.clear) + .frame(maxWidth: .infinity, minHeight: 1, maxHeight: 1) + .background(Color(hex: "555555")) + .padding(.vertical, 26.7) + + Text("주의사항") + .font(.custom(Font.bold.rawValue, size: 13.3)) + .foregroundColor(Color(hex: "eeeeee")) + + CanCouponNoticeItemView(notice: "이벤트 쿠폰을 통해 충전한 캔은 환불되지 않습니다.") + .padding(.top, 8) + + CanCouponNoticeItemView(notice: "쿠폰은 상업적 용도로 사용하거나 매매할 수 없습니다.") + .padding(.top, 4) + + CanCouponNoticeItemView(notice: "한번 등록한 쿠폰은 재사용이 불가합니다.") + .padding(.top, 4) + + CanCouponNoticeItemView(notice: "연령 제한 정책에 따라 쿠폰이용은 본인인증한 회원만 이용 가능합니다.") + .padding(.top, 4) + } + .padding(.horizontal, 13.3) + .padding(.vertical, 26.7) + .frame(maxWidth: .infinity, alignment: .leading) + .background(Color(hex: "13181b")) + .cornerRadius(6.7) + .padding(.top, 21.3) + } + .padding(13.3) + } + } + .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 CanCouponView_Previews: PreviewProvider { + static var previews: some View { + CanCouponView() + } +} diff --git a/SodaLive/Sources/MyPage/Can/Coupon/CanCouponViewModel.swift b/SodaLive/Sources/MyPage/Can/Coupon/CanCouponViewModel.swift new file mode 100644 index 0000000..b3ca551 --- /dev/null +++ b/SodaLive/Sources/MyPage/Can/Coupon/CanCouponViewModel.swift @@ -0,0 +1,65 @@ +// +// CanCouponViewModel.swift +// SodaLive +// +// Created by klaus on 2024/01/03. +// + +import Foundation +import Combine + +final class CanCouponViewModel: ObservableObject { + + private let repository = CanRepository() + private var subscription = Set() + + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false + + @Published var couponNumber = "" + + func useCoupon() { + if !isLoading { + isLoading = true + repository.useCanCoupon(couponNumber: couponNumber) + .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.errorMessage = "해당 쿠폰의 캔이 충전되었습니다." + + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + AppState.shared.setAppStep(step: .canStatus(refresh: {})) + } + } 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/MyPage/Can/Coupon/UseCanCouponRequest.swift b/SodaLive/Sources/MyPage/Can/Coupon/UseCanCouponRequest.swift new file mode 100644 index 0000000..c456ea3 --- /dev/null +++ b/SodaLive/Sources/MyPage/Can/Coupon/UseCanCouponRequest.swift @@ -0,0 +1,10 @@ +// +// UseCanCouponRequest.swift +// SodaLive +// +// Created by klaus on 2024/01/03. +// + +struct UseCanCouponRequest: Encodable { + let couponNumber: String +} diff --git a/SodaLive/Sources/MyPage/CanCardView.swift b/SodaLive/Sources/MyPage/CanCardView.swift index 931da83..763ce85 100644 --- a/SodaLive/Sources/MyPage/CanCardView.swift +++ b/SodaLive/Sources/MyPage/CanCardView.swift @@ -52,9 +52,9 @@ struct CanCardView: View { } } .padding(.horizontal, 13.3) - .padding(.vertical, 16.7) + .padding(.vertical, 10) .background(Color(hex: "222222")) - .cornerRadius(16.7) + .cornerRadius(6.7) } } diff --git a/SodaLive/Sources/MyPage/MyPageView.swift b/SodaLive/Sources/MyPage/MyPageView.swift index 1302385..2838c91 100644 --- a/SodaLive/Sources/MyPage/MyPageView.swift +++ b/SodaLive/Sources/MyPage/MyPageView.swift @@ -87,6 +87,15 @@ struct MyPageView: View { .frame(width: screenSize().width - 26.7) .padding(.top, 26.7) + if data.isAuth { + CanChargeCouponButtonView() + .frame(width: screenSize().width - 26.7) + .padding(.top, 13.3) + .onTapGesture { + AppState.shared.setAppStep(step: .canCoupon) + } + } + ReservationStatusView(data: data) .padding(.top, 33.3) diff --git a/SodaLive/Sources/MyPage/ReservationStatusView.swift b/SodaLive/Sources/MyPage/ReservationStatusView.swift index 3e3ef16..d7d55e8 100644 --- a/SodaLive/Sources/MyPage/ReservationStatusView.swift +++ b/SodaLive/Sources/MyPage/ReservationStatusView.swift @@ -25,8 +25,8 @@ struct ReservationStatusView: View { .frame(width: 20, height: 20) Text("라이브") - .font(.custom(Font.medium.rawValue, size: 14.7)) - .foregroundColor(Color(hex: "eeeeee")) + .font(.custom(Font.bold.rawValue, size: 14.7)) + .foregroundColor(Color(hex: "3bb9f1")) Text("\(data.liveReservationCount)") .font(.custom(Font.medium.rawValue, size: 14.7))