feat(creator): 대화하기 액션을 연결한다

This commit is contained in:
Yu Sung
2026-07-03 18:13:04 +09:00
parent 3f24788c29
commit 37c34ae91e
13 changed files with 242 additions and 24 deletions

View File

@@ -3,6 +3,7 @@ import Kingfisher
struct CreatorChannelHeaderSection: View {
let creator: CreatorChannelCreatorResponse
let onTapTalk: () -> Void
var body: some View {
ZStack(alignment: .bottom) {
@@ -49,7 +50,10 @@ struct CreatorChannelHeaderSection: View {
.foregroundColor(.white)
.multilineTextAlignment(.center)
talkButton
CreatorChannelTalkActionSection(
isAiChatAvailable: creator.isAiChatAvailable,
onTapTalk: onTapTalk
)
}
.frame(maxWidth: .infinity)
.padding(.horizontal, SodaSpacing.s24)
@@ -57,24 +61,4 @@ struct CreatorChannelHeaderSection: View {
}
.frame(width: screenSize().width, height: screenSize().width)
}
private var talkButton: some View {
Button(action: {}) {
HStack(spacing: SodaSpacing.s6) {
Image("ic_new_talk")
.resizable()
.frame(width: 20, height: 20)
Text(I18n.CreatorChannelHome.talk)
.appFont(size: 16, weight: .medium)
.foregroundColor(.white)
}
.padding(SodaSpacing.s12)
.overlay(
Capsule()
.stroke(Color.white.opacity(0.3), lineWidth: 1)
)
}
.buttonStyle(.plain)
}
}

View File

@@ -1,13 +1,22 @@
import SwiftUI
import Bootpay
import BootpayUI
struct CreatorChannelView: View {
let creatorId: Int
@StateObject private var viewModel = CreatorChannelViewModel()
@StateObject private var channelDonationViewModel = ChannelDonationViewModel()
@StateObject private var mypageViewModel = MyPageViewModel()
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
@AppStorage("auth") private var auth: Bool = UserDefaults.bool(forKey: UserDefaultsKey.auth)
@Environment(\.dismiss) private var dismiss
@State private var tabBarMinY: CGFloat = .greatestFiniteMagnitude
@State private var isShowChannelDonationDialog = false
@State private var isShowAuthView = false
@State private var isShowAuthConfirmView = false
@State private var pendingAction: (() -> Void)? = nil
@State private var payload = Payload()
private let titleBarHeight: CGFloat = 56
private let tabBarHeight: CGFloat = 52
@@ -68,6 +77,10 @@ struct CreatorChannelView: View {
LoadingView()
}
if isShowAuthConfirmView {
authConfirmDialog
}
if isShowChannelDonationDialog {
LiveRoomDonationDialogView(
isShowing: $isShowChannelDonationDialog,
@@ -92,7 +105,11 @@ struct CreatorChannelView: View {
}
}
.navigationBarBackButtonHidden(true)
.fullScreenCover(isPresented: $isShowAuthView) {
authView
}
.onAppear {
configurePayload()
if viewModel.hasLoaded == false {
viewModel.fetchHome(creatorId: creatorId)
}
@@ -129,7 +146,7 @@ struct CreatorChannelView: View {
@ViewBuilder
private var headerSection: some View {
if let creator = viewModel.response?.creator {
CreatorChannelHeaderSection(creator: creator)
CreatorChannelHeaderSection(creator: creator, onTapTalk: handleTalkTap)
}
}
@@ -168,6 +185,57 @@ struct CreatorChannelView: View {
}
}
private var authConfirmDialog: some View {
SodaDialog(
title: I18n.Chat.Auth.dialogTitle,
desc: I18n.Chat.Auth.dialogDescription,
confirmButtonTitle: I18n.Chat.Auth.goToVerification,
confirmButtonAction: {
isShowAuthConfirmView = false
isShowAuthView = true
},
cancelButtonTitle: I18n.Common.cancel,
cancelButtonAction: {
isShowAuthConfirmView = false
pendingAction = nil
},
textAlignment: .center
)
}
private var authView: some View {
BootpayUI(payload: payload, requestType: BootpayRequest.TYPE_AUTHENTICATION)
.onConfirm { _ in true }
.onCancel { _ in isShowAuthView = false }
.onError { _ in
AppState.shared.errorMessage = I18n.Chat.Auth.authenticationError
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 configurePayload() {
payload.applicationId = BOOTPAY_APP_ID
payload.price = 0
payload.pg = "다날"
payload.method = "본인인증"
payload.orderName = "본인인증"
payload.authenticationId = "\(UserDefaults.string(forKey: .nickname))__\(String(NSTimeIntervalSince1970))"
}
private func selectTab(_ tab: CreatorChannelTab) {
switch tab {
case .audio:
@@ -228,6 +296,43 @@ struct CreatorChannelView: View {
}
}
private func handleTalkTap() {
guard let characterId = viewModel.response?.creator.characterId, characterId > 0 else { return }
let trimmed = token.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else {
AppState.shared.setAppStep(step: .login)
return
}
let normalizedCountryCode = UserDefaults
.string(forKey: .countryCode)
.trimmingCharacters(in: .whitespacesAndNewlines)
.uppercased()
let isKoreanCountry = normalizedCountryCode.isEmpty || normalizedCountryCode == "KR"
if isKoreanCountry && auth == false {
pendingAction = {
AppState.shared.setAppStep(step: .characterDetail(characterId: characterId))
}
isShowAuthConfirmView = true
return
}
if !UserDefaults.isAdultContentVisible() {
pendingAction = nil
moveToContentSettingsWithGuideToast()
return
}
AppState.shared.setAppStep(step: .characterDetail(characterId: characterId))
}
private func moveToContentSettingsWithGuideToast() {
AppState.shared.setPendingContentSettingsGuideMessage(I18n.Settings.adultContentEnableGuide)
AppState.shared.setAppStep(step: .contentViewSettings)
}
private func showDonationDialog() {
guard let creatorId = viewModel.response?.creator.creatorId else { return }
channelDonationViewModel.setCreatorId(creatorId, shouldFetch: false)

View File

@@ -0,0 +1,37 @@
import SwiftUI
struct CreatorChannelTalkActionSection: View {
let isAiChatAvailable: Bool
let onTapTalk: () -> Void
var body: some View {
HStack {
Spacer()
if isAiChatAvailable {
Button(action: onTapTalk) {
HStack(spacing: SodaSpacing.s6) {
Image("ic_new_talk")
.resizable()
.frame(width: 20, height: 20)
Text(I18n.CreatorChannelHome.talk)
.appFont(size: 16, weight: .medium)
.foregroundColor(.white)
}
.padding(SodaSpacing.s12)
.overlay(
Capsule()
.stroke(Color.white.opacity(0.3), lineWidth: 1)
)
}
.buttonStyle(.plain)
}
Spacer()
}
.frame(height: 44)
.frame(maxWidth: .infinity)
.padding(.bottom, 14)
}
}