feat: 마이페이지
- 신규 UI
This commit is contained in:
		@@ -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..<recentContentList.count, id: \.self) { index in
 | 
			
		||||
                        RecentItemView()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .padding(.horizontal, 24)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct RecentItemView: View {
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        VStack(spacing: 8) {
 | 
			
		||||
            // Thumbnail placeholder
 | 
			
		||||
            RoundedRectangle(cornerRadius: 16)
 | 
			
		||||
                .fill(Color.gray)
 | 
			
		||||
                .frame(width: 168, height: 168)
 | 
			
		||||
                .overlay(
 | 
			
		||||
                    VStack {
 | 
			
		||||
                        Spacer()
 | 
			
		||||
                        HStack {
 | 
			
		||||
                            Spacer()
 | 
			
		||||
                            Text("00:12:33")
 | 
			
		||||
                                .font(.system(size: 12))
 | 
			
		||||
                                .foregroundColor(.white)
 | 
			
		||||
                                .padding(.horizontal, 10)
 | 
			
		||||
                                .padding(.vertical, 3)
 | 
			
		||||
                                .background(Color.black.opacity(0.7))
 | 
			
		||||
                                .cornerRadius(39)
 | 
			
		||||
                        }
 | 
			
		||||
                        .padding(8)
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
                .overlay(
 | 
			
		||||
                    VStack {
 | 
			
		||||
                        HStack {
 | 
			
		||||
                            Text("신작")
 | 
			
		||||
                                .font(.system(size: 12))
 | 
			
		||||
                                .foregroundColor(.white)
 | 
			
		||||
                                .padding(.horizontal, 10)
 | 
			
		||||
                                .padding(.vertical, 3)
 | 
			
		||||
                                .background(LinearGradient(
 | 
			
		||||
                                    gradient: Gradient(colors: [Color(hex: "0001B1"), Color(hex: "3B5FF1")]),
 | 
			
		||||
                                    startPoint: .leading,
 | 
			
		||||
                                    endPoint: .trailing
 | 
			
		||||
                                ))
 | 
			
		||||
                                .cornerRadius(12)
 | 
			
		||||
                            
 | 
			
		||||
                            Spacer()
 | 
			
		||||
                            
 | 
			
		||||
                            Image(systemName: "shield.fill")
 | 
			
		||||
                                .foregroundColor(.red)
 | 
			
		||||
                                .frame(width: 20, height: 20)
 | 
			
		||||
                        }
 | 
			
		||||
                        .padding(8)
 | 
			
		||||
                        
 | 
			
		||||
                        Spacer()
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            
 | 
			
		||||
            VStack(alignment: .leading, spacing: 4) {
 | 
			
		||||
                Text("우디(Woody)")
 | 
			
		||||
                    .font(.system(size: 18))
 | 
			
		||||
                    .foregroundColor(.white)
 | 
			
		||||
                
 | 
			
		||||
                Text("우기라스")
 | 
			
		||||
                    .font(.system(size: 14))
 | 
			
		||||
                    .foregroundColor(Color(hex: "78909C"))
 | 
			
		||||
            }
 | 
			
		||||
            .padding(.leading, 6)
 | 
			
		||||
        }
 | 
			
		||||
        .frame(width: 168)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct MyPageView_Previews: PreviewProvider {
 | 
			
		||||
    static var previews: some View {
 | 
			
		||||
        MyPageView()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user