From 652fe3dc133a0dddc8498eca5179f1e4c5be5259 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Tue, 3 Feb 2026 11:28:13 +0900 Subject: [PATCH] =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=EB=B3=B4=EA=B8=B0=20-=20=EC=84=B1=EC=9D=B8=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=20=EC=9E=85=EC=9E=A5=EC=97=90=20?= =?UTF-8?q?=EB=B3=B8=EC=9D=B8=EC=9D=B8=EC=A6=9D=20=ED=9D=90=EB=A6=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Live/Now/All/LiveNowAllView.swift | 200 +++++++++++++----- SodaLive/Sources/Main/Home/HomeView.swift | 4 +- 2 files changed, 146 insertions(+), 58 deletions(-) diff --git a/SodaLive/Sources/Live/Now/All/LiveNowAllView.swift b/SodaLive/Sources/Live/Now/All/LiveNowAllView.swift index d26118b..44868a7 100644 --- a/SodaLive/Sources/Live/Now/All/LiveNowAllView.swift +++ b/SodaLive/Sources/Live/Now/All/LiveNowAllView.swift @@ -7,14 +7,25 @@ import SwiftUI import RefreshableScrollView +import Bootpay +import BootpayUI struct LiveNowAllView: View { @StateObject var viewModel = LiveViewModel() @StateObject var liveAllViewModel = LiveAllViewModel() + @StateObject var mypageViewModel = MyPageViewModel() let spacing: CGFloat = 16 + @AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token) + @AppStorage("auth") private var auth: Bool = UserDefaults.bool(forKey: UserDefaultsKey.auth) + + @State private var isShowAuthView = false + @State private var isShowAuthConfirmView = false + @State private var pendingAction: (() -> Void)? = nil + @State private var payload = Payload() + var columns: [GridItem] { [ GridItem(.flexible(), spacing: spacing, alignment: .top), @@ -26,73 +37,150 @@ struct LiveNowAllView: View { var body: some View { BaseView(isLoading: $viewModel.isLoading) { - GeometryReader { proxy in - VStack(spacing: 0) { - DetailNavigationBar(title: I18n.LiveNow.allTitle) - - RefreshableScrollView( - refreshing: $viewModel.isRefresh, - action: { - viewModel.page = 1 - viewModel.isLast = false - viewModel.getLiveNowList() - }, - content: { - let itemWidth = (proxy.size.width - (spacing * 3)) / 2 - - LazyVGrid(columns: columns, spacing: spacing) { - ForEach(0.. 0 { + Rectangle() + .foregroundColor(Color.black.opacity(0)) + .frame(width: screenSize().width, height: 15.3) + } + } + .edgesIgnoringSafeArea(.bottom) + } + + if liveAllViewModel.isShowLiveDetail { + LiveDetailView( + roomId: liveAllViewModel.selectedRoomId, + onClickParticipant: { + AppState.shared.isShowPlayer = false + onClickParticipant(liveAllViewModel.selectedRoomId) + }, + onClickReservation: {}, + onClickStart: {}, + onClickCancel: {}, + onClickClose: { + withAnimation { + liveAllViewModel.isShowLiveDetail = false } - .padding(.horizontal, spacing) - .padding(.top, spacing) } ) - - if proxy.safeAreaInsets.bottom > 0 { - Rectangle() - .foregroundColor(Color.black.opacity(0)) - .frame(width: screenSize().width, height: 15.3) - } } - .edgesIgnoringSafeArea(.bottom) - } - - if liveAllViewModel.isShowLiveDetail { - LiveDetailView( - roomId: liveAllViewModel.selectedRoomId, - onClickParticipant: { - AppState.shared.isShowPlayer = false - onClickParticipant(liveAllViewModel.selectedRoomId) - }, - onClickReservation: {}, - onClickStart: {}, - onClickCancel: {}, - onClickClose: { - withAnimation { - liveAllViewModel.isShowLiveDetail = false - } - } - ) + + if isShowAuthConfirmView { + SodaDialog( + title: "본인인증", + desc: "청소년 보호를 위해\n본인인증을 완료한\n성인만 라이브 입장이 가능합니다.\n" + + "라이브 입장을 위해\n본인인증을 진행해 주세요.", + confirmButtonTitle: "본인인증 하러가기", + confirmButtonAction: { + isShowAuthConfirmView = false + isShowAuthView = true + }, + cancelButtonTitle: "취소", + cancelButtonAction: { + isShowAuthConfirmView = false + pendingAction = nil + }, + textAlignment: .center + ) + } } } .onAppear { + payload.applicationId = BOOTPAY_APP_ID + payload.price = 0 + payload.pg = "다날" + payload.method = "본인인증" + payload.orderName = "본인인증" + payload.authenticationId = "\(UserDefaults.string(forKey: .nickname))__\(String(NSTimeIntervalSince1970))" + viewModel.getLiveNowList() } + .fullScreenCover(isPresented: $isShowAuthView) { + BootpayUI(payload: payload, requestType: BootpayRequest.TYPE_AUTHENTICATION) + .onConfirm { _ in + true + } + .onCancel { _ in + isShowAuthView = false + } + .onError { _ in + AppState.shared.errorMessage = "본인인증 중 오류가 발생했습니다." + AppState.shared.isShowErrorPopup = true + isShowAuthView = false + } + .onDone { + DEBUG_LOG("onDone: \($0)") + mypageViewModel.authVerify($0) { + auth = true + isShowAuthView = false + if let action = pendingAction { + pendingAction = nil + action() + } + } + } + .onClose { + isShowAuthView = false + } + } + } + + private func handleLiveNowItemTap(roomId: Int, isAdult: Bool) { + let trimmed = token.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmed.isEmpty else { + AppState.shared.setAppStep(step: .login) + return + } + if isAdult && auth == false { + pendingAction = { openLiveDetail(roomId: roomId) } + isShowAuthConfirmView = true + return + } + openLiveDetail(roomId: roomId) + } + + private func openLiveDetail(roomId: Int) { + liveAllViewModel.selectedRoomId = roomId + withAnimation { + liveAllViewModel.isShowLiveDetail = true + } } } diff --git a/SodaLive/Sources/Main/Home/HomeView.swift b/SodaLive/Sources/Main/Home/HomeView.swift index 959972c..5943006 100644 --- a/SodaLive/Sources/Main/Home/HomeView.swift +++ b/SodaLive/Sources/Main/Home/HomeView.swift @@ -202,8 +202,8 @@ struct HomeView: View { if isShowAuthConfirmView { SodaDialog( title: "본인인증", - desc: "보이스온의 오픈월드 캐릭터톡은\n청소년 보호를 위해 본인인증한\n성인만 이용이 가능합니다.\n" + - "캐릭터톡 서비스를 이용하시려면\n본인인증을 하고 이용해주세요.", + desc: "청소년 보호를 위해\n본인인증을 완료한\n성인만 라이브 입장이 가능합니다.\n" + + "라이브 입장을 위해\n본인인증을 진행해 주세요.", confirmButtonTitle: "본인인증 하러가기", confirmButtonAction: { isShowAuthConfirmView = false