diff --git a/SodaLive/Resources/Assets.xcassets/ic_can.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_can.imageset/Contents.json index 5ba6682..113cff2 100644 --- a/SodaLive/Resources/Assets.xcassets/ic_can.imageset/Contents.json +++ b/SodaLive/Resources/Assets.xcassets/ic_can.imageset/Contents.json @@ -1,6 +1,7 @@ { "images" : [ { + "filename" : "ic_can.png", "idiom" : "universal", "scale" : "1x" }, @@ -9,7 +10,6 @@ "scale" : "2x" }, { - "filename" : "ic_can.png", "idiom" : "universal", "scale" : "3x" } diff --git a/SodaLive/Resources/Assets.xcassets/ic_can.imageset/ic_can.png b/SodaLive/Resources/Assets.xcassets/ic_can.imageset/ic_can.png index 7142a77..3ad945a 100644 Binary files a/SodaLive/Resources/Assets.xcassets/ic_can.imageset/ic_can.png and b/SodaLive/Resources/Assets.xcassets/ic_can.imageset/ic_can.png differ diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_auth.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_my_auth.imageset/Contents.json new file mode 100644 index 0000000..4208d3a --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/ic_my_auth.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_my_auth.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_auth.imageset/ic_my_auth.png b/SodaLive/Resources/Assets.xcassets/ic_my_auth.imageset/ic_my_auth.png new file mode 100644 index 0000000..d89be15 Binary files /dev/null and b/SodaLive/Resources/Assets.xcassets/ic_my_auth.imageset/ic_my_auth.png differ diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_block.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_my_block.imageset/Contents.json new file mode 100644 index 0000000..87cea1c --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/ic_my_block.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_my_block.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_block.imageset/ic_my_block.png b/SodaLive/Resources/Assets.xcassets/ic_my_block.imageset/ic_my_block.png new file mode 100644 index 0000000..4b01e96 Binary files /dev/null and b/SodaLive/Resources/Assets.xcassets/ic_my_block.imageset/ic_my_block.png differ diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_coupon.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_my_coupon.imageset/Contents.json new file mode 100644 index 0000000..10c2727 --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/ic_my_coupon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_my_coupon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_coupon.imageset/ic_my_coupon.png b/SodaLive/Resources/Assets.xcassets/ic_my_coupon.imageset/ic_my_coupon.png new file mode 100644 index 0000000..607ba2c Binary files /dev/null and b/SodaLive/Resources/Assets.xcassets/ic_my_coupon.imageset/ic_my_coupon.png differ diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_event.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_my_event.imageset/Contents.json new file mode 100644 index 0000000..6dd0a3d --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/ic_my_event.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_my_event.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_event.imageset/ic_my_event.png b/SodaLive/Resources/Assets.xcassets/ic_my_event.imageset/ic_my_event.png new file mode 100644 index 0000000..990b135 Binary files /dev/null and b/SodaLive/Resources/Assets.xcassets/ic_my_event.imageset/ic_my_event.png differ diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_notice.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_my_notice.imageset/Contents.json new file mode 100644 index 0000000..9a0e7db --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/ic_my_notice.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_my_notice.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_notice.imageset/ic_my_notice.png b/SodaLive/Resources/Assets.xcassets/ic_my_notice.imageset/ic_my_notice.png new file mode 100644 index 0000000..1557377 Binary files /dev/null and b/SodaLive/Resources/Assets.xcassets/ic_my_notice.imageset/ic_my_notice.png differ diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_service_center.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_my_service_center.imageset/Contents.json new file mode 100644 index 0000000..48d0064 --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/ic_my_service_center.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_my_service_center.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_service_center.imageset/ic_my_service_center.png b/SodaLive/Resources/Assets.xcassets/ic_my_service_center.imageset/ic_my_service_center.png new file mode 100644 index 0000000..ec2e225 Binary files /dev/null and b/SodaLive/Resources/Assets.xcassets/ic_my_service_center.imageset/ic_my_service_center.png differ diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_storage.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/ic_my_storage.imageset/Contents.json new file mode 100644 index 0000000..cb952fc --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/ic_my_storage.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_my_storage.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/ic_my_storage.imageset/ic_my_storage.png b/SodaLive/Resources/Assets.xcassets/ic_my_storage.imageset/ic_my_storage.png new file mode 100644 index 0000000..60e037a Binary files /dev/null and b/SodaLive/Resources/Assets.xcassets/ic_my_storage.imageset/ic_my_storage.png differ diff --git a/SodaLive/Resources/Assets.xcassets/img_introduce_voiceon.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/img_introduce_voiceon.imageset/Contents.json new file mode 100644 index 0000000..737c1a0 --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/img_introduce_voiceon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "img_introduce_voiceon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/img_introduce_voiceon.imageset/img_introduce_voiceon.png b/SodaLive/Resources/Assets.xcassets/img_introduce_voiceon.imageset/img_introduce_voiceon.png new file mode 100644 index 0000000..731ebd4 Binary files /dev/null and b/SodaLive/Resources/Assets.xcassets/img_introduce_voiceon.imageset/img_introduce_voiceon.png differ diff --git a/SodaLive/Sources/MyPage/MyPageView.swift b/SodaLive/Sources/MyPage/MyPageView.swift index a8c5eeb..8531337 100644 --- a/SodaLive/Sources/MyPage/MyPageView.swift +++ b/SodaLive/Sources/MyPage/MyPageView.swift @@ -10,6 +10,7 @@ import SwiftUI import Bootpay import BootpayUI import PopupView +import Kingfisher import RefreshableScrollView struct MyPageView: View { @@ -46,288 +47,81 @@ struct MyPageView: View { viewModel.isShowAuthView = false } } else { - GeometryReader { geo in - VStack { - HomeNavigationBar(title: "마이 페이지") { - if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - Image("ic_settings") - .resizable() - .frame(width: 20, height: 20) - .onTapGesture { - AppState.shared.setAppStep(step: .settings) - } - } - } - if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - ScrollView(.vertical, showsIndicators: false) { - VStack(spacing: 0) { - if let data = viewModel.myPageResponse { - MyInfoCardView(data: data) { - viewModel.getMypage() - } - .frame(width: screenSize().width - 26.7) - .padding(.top, 13.3) - - if UserDefaults.string(forKey: .role) == MemberRole.CREATOR.rawValue { - Text("내 채널 보기") - .frame(width: screenSize().width - 26.7, height: 46.7) - .font(.custom(Font.bold.rawValue, size: 15.3)) - .foregroundColor(Color.grayee) - .background(Color.button) - .cornerRadius(6.7) - .padding(.top, 26.7) - .onTapGesture { - AppState.shared.setAppStep(step: .creatorDetail(userId: UserDefaults.int(forKey: .userId))) - } - } - - HStack(spacing: 10.7) { - Text("팔로잉 리스트") - .font(.custom(Font.bold.rawValue, size: 14.7)) - .foregroundColor(Color.button) - .frame(maxWidth: .infinity) - .padding(.vertical, 13.3) - .background(Color.bg) - .cornerRadius(6.7) - .overlay( - RoundedRectangle(cornerRadius: 6.7) - .stroke(Color.button, lineWidth: 1.3) - ) - .contentShape(Rectangle()) - .onTapGesture { - AppState.shared.setAppStep(step: .followingList) - } - - Text("차단 리스트") - .font(.custom(Font.bold.rawValue, size: 14.7)) - .foregroundColor(Color.button) - .frame(maxWidth: .infinity) - .padding(.vertical, 13.3) - .background(Color.bg) - .cornerRadius(6.7) - .overlay( - RoundedRectangle(cornerRadius: 6.7) - .stroke(Color.button, lineWidth: 1.3) - ) - .contentShape(Rectangle()) - .onTapGesture { - AppState.shared.setAppStep(step: .blockList) - } - } - .padding(.top, 26.7) - .padding(.horizontal, 13.3) - - if UserDefaults.int(forKey: .userId) != 17958 { - CanCardView(data: data) { - viewModel.getMypage() - } - .frame(width: screenSize().width - 26.7) - .padding(.top, 26.7) - - HStack { - HStack(spacing: 6.7) { - Text("\(data.point)") - .font(.custom(Font.bold.rawValue, size: 18.3)) - .foregroundColor(.grayee) - - Image("ic_point") - .resizable() - .frame(width: 20, height: 20) - - Image("ic_forward") - .resizable() - .frame(width: 20, height: 20) - } - .onTapGesture { - AppState.shared.setAppStep(step: .pointStatus(refresh: { viewModel.getMypage() })) - } - - Spacer() - } - .padding(.horizontal, 13.3) - .padding(.vertical, 16.7) - .frame(width: screenSize().width - 26.7) - .background(Color.gray22) - .cornerRadius(6.7) - .padding(.top, 13.3) - } - - if data.isAuth { - CanChargeCouponButtonView() - .frame(width: screenSize().width - 26.7) - .padding(.top, 13.3) - .onTapGesture { - AppState.shared.setAppStep(step: .canCoupon(refresh: { - viewModel.getMypage() - })) - } - } - - VStack(alignment: .leading, spacing: 13.3) { - Text("내 보관함") - .font(.custom(Font.bold.rawValue, size: 18.3)) - .foregroundColor(Color.grayee) - - HStack(spacing: 10.7) { - Text("구매목록") - .font(.custom(Font.bold.rawValue, size: 14.7)) - .foregroundColor(Color.button) - .frame(maxWidth: .infinity) - .padding(.vertical, 13.3) - .background(Color.bg) - .cornerRadius(6.7) - .overlay( - RoundedRectangle(cornerRadius: 6.7) - .stroke(Color.button, lineWidth: 1.3) - ) - .contentShape(Rectangle()) - .onTapGesture { - AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist)) - } - - Text("재생목록") - .font(.custom(Font.bold.rawValue, size: 14.7)) - .foregroundColor(Color.button) - .frame(maxWidth: .infinity) - .padding(.vertical, 13.3) - .background(Color.bg) - .cornerRadius(6.7) - .overlay( - RoundedRectangle(cornerRadius: 6.7) - .stroke(Color.button, lineWidth: 1.3) - ) - .contentShape(Rectangle()) - .onTapGesture { - AppState.shared.setAppStep(step: .myBox(currentTab: .playlist)) - } - } - } - .padding(.top, 33) - .padding(.horizontal, 13.3) - - ReservationStatusView(data: data) - .padding(.top, 40) - - ServiceCenterButtonView() - .padding(.top, 40) - - if !data.isAuth { - AuthButtonView() - .padding(.top, 40) - .onTapGesture { - viewModel.isShowAuthView = true - } - } - - if let url = URL(string: "https://blog.naver.com/sodalive_official"), - UIApplication.shared.canOpenURL(url) { - Image("img_how_to_use") - .resizable() - .frame( - width: screenSize().width, - height: (200 * screenSize().width) / 1080 - ) - .padding(.vertical, 40) - .onTapGesture { - UIApplication.shared.open(url) - } - } - } - } - } - } else { - ScrollView(.vertical, showsIndicators: false) { - VStack(spacing: 24) { - HStack(spacing: 6.7) { - Image("ic_logo_circle_gray") - - Text("Login") - .font(.custom(Font.medium.rawValue, size: 32)) - .foregroundColor(.gray90) - } - .padding(20) - .frame(maxWidth: .infinity) - .background(Color.bg) - .cornerRadius(6.7) - .contentShape(Rectangle()) - .padding(.horizontal, 13.3) - .padding(.top, 24) - .onTapGesture { - AppState.shared - .setAppStep(step: .login) - } - - HStack(spacing: 0) { - HStack(spacing: 6.7) { - Text("0") - .font(.custom(Font.bold.rawValue, size: 18.3)) - .foregroundColor(.grayee) - - Image("ic_can") - .resizable() - .frame(width: 20, height: 20) - - Image("ic_forward") - .resizable() - .frame(width: 20, height: 20) - } - .onTapGesture { - AppState.shared - .setAppStep(step: .login) - } - - Spacer() - - HStack(spacing: 7) { - Image("ic_coin_w") - .resizable() - .frame(width: 26.7, height: 26.7) - - Text("충전") - .font(.custom(Font.bold.rawValue, size: 12)) - .foregroundColor(Color(hex: "b38fff")) - } - .padding(.horizontal, 11.3) - .padding(.vertical, 7) - .overlay( - RoundedRectangle(cornerRadius: CGFloat(16.7)) - .stroke(lineWidth: 1) - .foregroundColor(Color(hex: "b38fff")) - ) - .cornerRadius(16.7) - .onTapGesture { - AppState.shared - .setAppStep(step: .login) - } - } - .frame(maxWidth: .infinity) - .padding(.horizontal, 13.3) - .padding(.vertical, 10) - .background(Color.gray22) - .cornerRadius(6.7) - .padding(.horizontal, 13.3) - - ServiceCenterButtonView() - .padding(.horizontal, 13.3) - - if let url = URL(string: "https://blog.naver.com/sodalive_official"), - UIApplication.shared.canOpenURL(url) { - Image("img_how_to_use") - .resizable() - .frame( - width: screenSize().width, - height: (200 * screenSize().width) / 1080 - ) - .onTapGesture { - UIApplication.shared.open(url) - } - } - } - } - } + VStack(spacing: 0) { + // Header + MyPageHeaderView( + isShowButton: !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + ) + + if let notice = viewModel.latestNotice { + // Update Banner + UpdateBannerView(item: notice) + } + + ScrollView(.vertical, showsIndicators: false) { + VStack(spacing: 32) { + // Profile Section + if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + ProfileSectionView( + nickname: viewModel.nickname, + profileUrl: viewModel.profileUrl + ) { + viewModel.getMypage() + } + } else { + HStack { + Text("LOGIN") + .font(.custom(Font.preBold.rawValue, size: 32)) + .foregroundColor(Color.gray77) + } + .padding(.vertical, 12) + .frame(maxWidth: .infinity) + .background(Color.gray22) + .cornerRadius(16) + .onTapGesture { + AppState.shared + .setAppStep(step: .login) + } + } + + // Can & Point Cards + CanPointCardsView( + can: viewModel.chargeCan + viewModel.rewardCan, + point: viewModel.point, + token: token, + refresh: { viewModel.getMypage() } + ) + + if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + // Category Buttons + CategoryButtonsView( + isShowAuthView: $viewModel.isShowAuthView, + isAuthenticated: viewModel.isAuth, + refresh: { + viewModel.getMypage() + } + ) + } + + if let url = URL(string: "https://blog.naver.com/sodalive_official"), + UIApplication.shared.canOpenURL(url) { + // Voice On Banner + Image("img_introduce_voiceon") + .onTapGesture { + UIApplication.shared.open(url) + } + } + + if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !viewModel.recentContentList.isEmpty { + // Recent 10 Section + RecentContentSection(recentContentList: viewModel.recentContentList) + } + } + .padding(.horizontal, 24) + .padding(.vertical, 32) } - .frame(width: geo.size.width, height: geo.size.height) } + .background(Color(hex: "131313")) .onAppear { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { viewModel.getMypage() @@ -363,6 +157,370 @@ struct MyPageView: View { } } +// MARK: - Header View +struct MyPageHeaderView: View { + + let isShowButton: Bool + + var body: some View { + HStack { + // Logo + Image("img_text_logo") // 로고 이미지 위치 + + Spacer() + + if isShowButton { + HStack(spacing: 24) { + // Settings Icon + Image(systemName: "ic_settings") + .foregroundColor(.white) + .frame(width: 24, height: 24) + .onTapGesture { + AppState.shared.setAppStep(step: .settings) + } + } + } + } + .padding(.horizontal, 24) + .padding(.vertical, 20) + .background(Color.black) + } +} + +// MARK: - Update Banner View +struct UpdateBannerView: View { + + let item: NoticeItem + + var body: some View { + HStack { + Text("\(item.title)") + .font(.system(size: 16)) + .foregroundColor(Color(hex: "B0BEC5")) + + Spacer() + + HStack(spacing: 2) { + Text("자세히") + .font(.system(size: 16)) + .foregroundColor(Color(hex: "B0BEC5")) + + Image(systemName: "chevron.right") + .foregroundColor(Color(hex: "B0BEC5")) + .frame(width: 24, height: 24) + } + } + .padding(.horizontal, 24) + .padding(.vertical, 6) + .background(Color.black) + .contentShape(Rectangle()) + .onTapGesture { + AppState.shared.setAppStep(step: .noticeDetail(notice: item)) + } + } +} + +// MARK: - Profile Section View +struct ProfileSectionView: View { + + let nickname: String + let profileUrl: String + let refresh: () -> Void + + var body: some View { + VStack(spacing: 16) { + HStack(spacing: 20) { + // Profile Image Placeholder + KFImage(URL(string: profileUrl)) + .resizable() + .frame(width: 64, height: 64) + .clipShape(Circle()) + + VStack(alignment: .leading) { + Text("\(nickname)") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.white) + } + + Spacer() + + Button("프로필 수정") { + if AppState.shared.roomId <= 0 { + AppState.shared.setAppStep(step: .profileUpdate(refresh: refresh)) + } + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .background(Color(hex: "263238")) + .foregroundColor(.white) + .cornerRadius(9999) + .font(.system(size: 16)) + } + } + } +} + +// MARK: - Can Point Cards View +struct CanPointCardsView: View { + + let can: Int + let point: Int + let token: String + let refresh: () -> Void + + var body: some View { + // Can & Point Cards + VStack(spacing: 0) { + // Can Card + HStack { + HStack(spacing: 8) { + Image("ic_can") + + Text("\(can)") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.white) + + Image(systemName: "chevron.right") + .foregroundColor(Color(hex: "B0BEC5")) + .frame(width: 24, height: 24) + } + .onTapGesture { + if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + AppState.shared.setAppStep(step: .canStatus(refresh: refresh)) + } else { + AppState.shared + .setAppStep(step: .login) + } + } + + Spacer() + + if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + Button("캔 충전") { + AppState.shared.setAppStep(step: .canCharge(refresh: refresh)) + } + .padding(.horizontal, 16) + .padding(.vertical, 11) + .background(Color.button) + .cornerRadius(9999) + .foregroundColor(.white) + .font(.system(size: 16, weight: .bold)) + } else { + Text("") + .padding(.horizontal, 16) + .padding(.vertical, 11) + .background(Color.clear) + .foregroundColor(.white) + .font(.system(size: 16, weight: .bold)) + } + } + .padding(15) + + // Point Card + HStack { + HStack(spacing: 8) { + Image("ic_point") + + Text("\(point)") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.white) + + Image(systemName: "chevron.right") + .foregroundColor(Color(hex: "B0BEC5")) + .frame(width: 24, height: 24) + } + .onTapGesture { + if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + AppState.shared.setAppStep(step: .pointStatus(refresh: refresh)) + } else { + AppState.shared + .setAppStep(step: .login) + } + } + + Spacer() + + Text("") + .padding(.horizontal, 16) + .padding(.vertical, 11) + .background(Color.clear) + .foregroundColor(.white) + .font(.system(size: 16, weight: .bold)) + } + .padding(15) + } + .background(Color(hex: "263238")) + .cornerRadius(16) + } +} + +// MARK: - Category Buttons View +struct CategoryButtonsView: View { + @Binding var isShowAuthView: Bool + + let isAuthenticated: Bool + let refresh: () -> Void + + var body: some View { + LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 3), spacing: 14) { + CategoryButtonItem(icon: "ic_my_storage", title: "보관함") { + AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist)) + } + CategoryButtonItem(icon: "ic_my_block", title: "차단목록") { + AppState.shared.setAppStep(step: .blockList) + } + if isAuthenticated { + CategoryButtonItem( + icon: "ic_my_coupon", + title: "쿠폰등록" + ) { + AppState.shared.setAppStep(step: .canCoupon(refresh: refresh)) + } + } + + CategoryButtonItem(icon: "ic_my_notice", title: "공지사항") { + AppState.shared.setAppStep(step: .notices) + } + + CategoryButtonItem(icon: "ic_my_event", title: "이벤트") { + AppState.shared.setAppStep(step: .events) + } + + CategoryButtonItem(icon: "ic_my_service_center", title: "고객센터") { + AppState.shared.setAppStep(step: .serviceCenter) + } + + if !isAuthenticated { + CategoryButtonItem( + icon: "ic_my_auth", + title: "본인인증" + ) { + isShowAuthView = true + } + } + } + } +} + +struct CategoryButtonItem: View { + let icon: String + let title: String + let onClick: () -> Void + + var body: some View { + VStack(spacing: 12) { + // Icon Placeholder + RoundedRectangle(cornerRadius: 16) + .foregroundColor(Color(hex: "15202F")) + .frame(width: 76, height: 76) + .overlay{ + Image(icon) + } + + Text(title) + .font(.custom(Font.preRegular.rawValue, size: 14)) + .foregroundColor(.white) + } + .onTapGesture { + onClick() + } + } +} + +// MARK: - Recent 10 Content Section +struct RecentContentSection: View { + + let recentContentList: [AudioContentMainItem] + + var body: some View { + VStack(alignment: .leading, spacing: 14) { + HStack(spacing: 0) { + Text("최근 들은 ") + .font(.system(size: 16, weight: .bold)) + .foregroundColor(.white) + + Text("\(recentContentList.count)") + .font(.system(size: 16, weight: .bold)) + .foregroundColor(.white) + } + + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 16) { + ForEach(0..() - @Published var myPageResponse: MyPageResponse? = nil + @Published var latestNotice: NoticeItem? = nil + + @Published var nickname: String = "" + @Published var profileUrl: String = "" + @Published var chargeCan: Int = 0 + @Published var rewardCan: Int = 0 + @Published var point: Int = 0 + @Published var isAuth: Bool = false + @Published var recentContentList: [AudioContentMainItem] = [] @Published var errorMessage = "" @Published var isShowPopup = false @@ -32,7 +41,7 @@ final class MyPageViewModel: ObservableObject { case .failure(let error): ERROR_LOG(error.localizedDescription) } - } receiveValue: { response in + } receiveValue: {[unowned self] response in self.isLoading = false let responseData = response.data @@ -41,7 +50,12 @@ final class MyPageViewModel: ObservableObject { let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) if let data = decoded.data, decoded.success { - self.myPageResponse = data + self.nickname = data.nickname + self.profileUrl = data.profileUrl + self.chargeCan = data.chargeCan + self.rewardCan = data.rewardCan + self.point = data.point + self.isAuth = data.isAuth } else { if let message = decoded.message { self.errorMessage = message