// // CreatorDetailDialogView.swift // SodaLive // // Created by klaus on 2/25/26. // import SwiftUI import UIKit import Kingfisher struct CreatorDetailDialogView: View { @Binding var isShowing: Bool let creatorDetail: GetCreatorDetailResponse? let isLoading: Bool @Environment(\.openURL) private var openURL private var closeIconAssetName: String { UIImage(named: "ic_x_white") != nil ? "ic_x_white" : "ic_close_white" } private var profileImageSize: CGFloat { screenSize().width - 26.7 - 48 } private var snsItems: [CreatorDetailSnsItem] { guard let creatorDetail else { return [] } var items = [CreatorDetailSnsItem]() appendSnsItem(items: &items, iconName: "ic_sns_instagram", url: creatorDetail.instagramUrl) appendSnsItem(items: &items, iconName: "ic_sns_fancimm", url: creatorDetail.fancimmUrl) appendSnsItem(items: &items, iconName: "ic_sns_x", url: creatorDetail.xurl) appendSnsItem(items: &items, iconName: "ic_sns_youtube", url: creatorDetail.youtubeUrl) appendSnsItem(items: &items, iconName: "ic_sns_kakao", url: creatorDetail.kakaoOpenChatUrl) return items } var body: some View { ZStack { Color.black .opacity(0.7) .ignoresSafeArea() .onTapGesture { isShowing = false } VStack(alignment: .leading, spacing: 0) { HStack(spacing: 0) { Spacer() Image(closeIconAssetName) .resizable() .frame(width: 24, height: 24) .contentShape(Rectangle()) .onTapGesture { isShowing = false } } .padding(.top, 24) .padding(.trailing, 24) VStack(alignment: .leading, spacing: 0) { if isLoading { LoadingView() .frame(maxWidth: .infinity) .frame(height: 240) } else if let creatorDetail { ScrollView(.vertical, showsIndicators: false) { VStack(alignment: .leading, spacing: 0) { KFImage(URL(string: creatorDetail.profileImageUrl)) .cancelOnDisappear(true) .downsampling(size: CGSize(width: profileImageSize, height: profileImageSize)) .resizable() .scaledToFill() .frame(width: profileImageSize, height: profileImageSize) .clipped() .cornerRadius(16) Text(creatorDetail.nickname) .appFont(size: 36, weight: .bold) .foregroundColor(.white) .padding(.top, 24) VStack(alignment: .leading, spacing: 30) { detailSection( title: I18n.MemberChannel.creatorDetailDebut, value: debutDisplayValue(creatorDetail: creatorDetail) ) detailSection( title: I18n.MemberChannel.creatorDetailTotalLiveCount, value: creatorDetail.activitySummary.liveCount.comma() ) detailSection( title: I18n.MemberChannel.creatorDetailAccumulatedLiveTime, value: creatorDetail.activitySummary.liveTime.comma() ) detailSection( title: I18n.MemberChannel.creatorDetailAccumulatedParticipants, value: creatorDetail.activitySummary.liveContributorCount.comma() ) detailSection( title: I18n.MemberChannel.creatorDetailRegisteredContentCount, value: creatorDetail.activitySummary.contentCount.comma() ) if !snsItems.isEmpty { snsSection(items: snsItems) } } .padding(.top, 30) } } .frame(maxHeight: screenSize().height * 0.72) } } .padding(.horizontal, 24) .padding(.top, 12) .padding(.bottom, 24) } .background(Color.gray22) .cornerRadius(8) .padding(.horizontal, 13.3) } } @ViewBuilder private func detailSection(title: String, value: String) -> some View { VStack(alignment: .leading, spacing: 8) { Text(title) .appFont(size: 16, weight: .medium) .foregroundColor(Color(hex: "B0BEC5")) Text(value) .appFont(size: 20, weight: .medium) .foregroundColor(.white) } } @ViewBuilder private func snsSection(items: [CreatorDetailSnsItem]) -> some View { VStack(alignment: .leading, spacing: 8) { Text(I18n.MemberChannel.creatorDetailSns) .appFont(size: 16, weight: .medium) .foregroundColor(Color(hex: "B0BEC5")) HStack(spacing: 12) { ForEach(items) { item in Image(item.iconName) .resizable() .frame(width: 32, height: 32) .contentShape(Rectangle()) .onTapGesture { openSnsLink(item.url) } } } } } private func debutDisplayValue(creatorDetail: GetCreatorDetailResponse) -> String { let debutDate = creatorDetail.debutDate.trimmingCharacters(in: .whitespacesAndNewlines) let dday = creatorDetail.dday.trimmingCharacters(in: .whitespacesAndNewlines) guard !debutDate.isEmpty, !dday.isEmpty else { return I18n.MemberChannel.preDebut } return "\(debutDate) (\(dday))" } private func appendSnsItem(items: inout [CreatorDetailSnsItem], iconName: String, url: String) { let trimmed = url.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return } items.append(CreatorDetailSnsItem(iconName: iconName, url: trimmed)) } private func openSnsLink(_ urlString: String) { guard let url = normalizedUrl(urlString) else { return } openURL(url) } private func normalizedUrl(_ urlString: String) -> URL? { let trimmed = urlString.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return nil } if trimmed.hasPrefix("http://") || trimmed.hasPrefix("https://") { return URL(string: trimmed) } return URL(string: "https://\(trimmed)") } } private struct CreatorDetailSnsItem: Identifiable { let iconName: String let url: String var id: String { iconName } }