feat(home): 홈 탭에 인기 캐릭터 순위 추가

This commit is contained in:
Yu Sung
2025-10-20 18:44:22 +09:00
parent 731ef8cee1
commit 721a26bb7b
4 changed files with 105 additions and 1 deletions

View File

@@ -15,6 +15,7 @@ struct GetHomeResponse: Decodable {
let originalAudioDramaList: [SeriesListItem] let originalAudioDramaList: [SeriesListItem]
let auditionList: [GetAuditionListItem] let auditionList: [GetAuditionListItem]
let dayOfWeekSeriesList: [SeriesListItem] let dayOfWeekSeriesList: [SeriesListItem]
let popularCharacters: [Character]
let contentRanking: [GetAudioContentRankingItem] let contentRanking: [GetAudioContentRankingItem]
let recommendChannelList: [RecommendChannelResponse] let recommendChannelList: [RecommendChannelResponse]
let freeContentList: [AudioContentMainItem] let freeContentList: [AudioContentMainItem]

View File

@@ -6,6 +6,8 @@
// //
import SwiftUI import SwiftUI
import Bootpay
import BootpayUI
struct HomeTabView: View { struct HomeTabView: View {
@StateObject var viewModel = HomeTabViewModel() @StateObject var viewModel = HomeTabViewModel()
@@ -13,6 +15,32 @@ struct HomeTabView: View {
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token) @AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
@AppStorage("role") private var role: String = UserDefaults.string(forKey: UserDefaultsKey.role) @AppStorage("role") private var role: String = UserDefaults.string(forKey: UserDefaultsKey.role)
@AppStorage("auth") private var auth: Bool = UserDefaults.bool(forKey: UserDefaultsKey.auth)
@State private var isShowAuthView: Bool = false
@State private var isShowAuthConfirmView: Bool = false
@State private var pendingAction: (() -> Void)? = nil
@State private var payload = Payload()
var onTapPopularCharacterAllView: (() -> Void)? = nil
// CharacterView
private func handleCharacterSelection(_ characterId: Int) {
let trimmed = token.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else {
AppState.shared.setAppStep(step: .login)
return
}
if auth == false {
pendingAction = {
AppState.shared
.setAppStep(step: .characterDetail(characterId: characterId))
}
isShowAuthConfirmView = true
return
}
AppState.shared.setAppStep(step: .characterDetail(characterId: characterId))
}
var body: some View { var body: some View {
BaseView(isLoading: $viewModel.isLoading) { BaseView(isLoading: $viewModel.isLoading) {
@@ -205,6 +233,24 @@ struct HomeTabView: View {
viewModel.getDayOfWeekSeriesList(dayOfWeek: $0) viewModel.getDayOfWeekSeriesList(dayOfWeek: $0)
} }
//
if !viewModel.popularCharacters.isEmpty {
CharacterSectionView(
title: "인기 캐릭터",
items: viewModel.popularCharacters,
isShowRank: true,
trailingTitle: "전체보기",
onTapTrailing: {
if let onTapPopularCharacterAllView = onTapPopularCharacterAllView {
onTapPopularCharacterAllView()
}
},
onTap: { ch in
handleCharacterSelection(ch.characterId)
}
)
}
if !viewModel.contentRanking.isEmpty { if !viewModel.contentRanking.isEmpty {
HomeWeeklyChartView(contentList: viewModel.contentRanking) HomeWeeklyChartView(contentList: viewModel.contentRanking)
} }
@@ -332,10 +378,61 @@ struct HomeTabView: View {
AppState.shared.setAppStep(step: .createContent) AppState.shared.setAppStep(step: .createContent)
} }
} }
if isShowAuthConfirmView {
SodaDialog(
title: "본인인증",
desc: "보이스온의 오픈월드 캐릭터톡은\n청소년 보호를 위해 본인인증한\n성인만 이용이 가능합니다.\n" +
"캐릭터톡 서비스를 이용하시려면\n본인인증을 하고 이용해주세요.",
confirmButtonTitle: "본인인증 하러가기",
confirmButtonAction: {
isShowAuthConfirmView = false
isShowAuthView = true
},
cancelButtonTitle: "취소",
cancelButtonAction: {
isShowAuthConfirmView = false
pendingAction = nil
},
textAlignment: .center
)
}
} }
.onAppear { .onAppear {
payload.applicationId = BOOTPAY_APP_ID
payload.price = 0
payload.pg = "다날"
payload.method = "본인인증"
payload.orderName = "본인인증"
payload.authenticationId = "\(UserDefaults.string(forKey: .nickname))__\(String(NSTimeIntervalSince1970))"
viewModel.fetchData() viewModel.fetchData()
} }
.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 { _ in
auth = true
isShowAuthView = false
if let action = pendingAction {
pendingAction = nil
action()
}
}
.onClose {
isShowAuthView = false
}
}
} }
.popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) {
HStack { HStack {

View File

@@ -28,6 +28,7 @@ final class HomeTabViewModel: ObservableObject {
@Published var eventBannerList: [GetAudioContentBannerResponse] = [] @Published var eventBannerList: [GetAudioContentBannerResponse] = []
@Published var originalAudioDramaList: [SeriesListItem] = [] @Published var originalAudioDramaList: [SeriesListItem] = []
@Published var dayOfWeekSeriesList: [SeriesListItem] = [] @Published var dayOfWeekSeriesList: [SeriesListItem] = []
@Published private(set) var popularCharacters: [Character] = []
@Published var contentRanking: [GetAudioContentRankingItem] = [] @Published var contentRanking: [GetAudioContentRankingItem] = []
@Published var recommendChannelList: [RecommendChannelResponse] = [] @Published var recommendChannelList: [RecommendChannelResponse] = []
@Published var freeContentList: [AudioContentMainItem] = [] @Published var freeContentList: [AudioContentMainItem] = []
@@ -59,6 +60,7 @@ final class HomeTabViewModel: ObservableObject {
self.eventBannerList = data.bannerList self.eventBannerList = data.bannerList
self.originalAudioDramaList = data.originalAudioDramaList self.originalAudioDramaList = data.originalAudioDramaList
self.dayOfWeekSeriesList = data.dayOfWeekSeriesList self.dayOfWeekSeriesList = data.dayOfWeekSeriesList
self.popularCharacters = data.popularCharacters
self.contentRanking = data.contentRanking self.contentRanking = data.contentRanking
self.recommendChannelList = data.recommendChannelList self.recommendChannelList = data.recommendChannelList
self.freeContentList = data.freeContentList self.freeContentList = data.freeContentList

View File

@@ -18,7 +18,7 @@ struct HomeView: View {
@StateObject var contentPlayerPlayManager = ContentPlayerPlayManager.shared @StateObject var contentPlayerPlayManager = ContentPlayerPlayManager.shared
private let liveView = LiveView() private let liveView = LiveView()
private let homeTabView = HomeTabView() @State var homeTabView = HomeTabView()
private let chatTabView = ChatTabView() private let chatTabView = ChatTabView()
@State private var isShowPlayer = false @State private var isShowPlayer = false
@@ -166,6 +166,10 @@ struct HomeView: View {
} }
} }
.onAppear { .onAppear {
homeTabView.onTapPopularCharacterAllView = {
viewModel.currentTab = .chat
}
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
pushTokenUpdate() pushTokenUpdate()
viewModel.getMemberInfo() viewModel.getMemberInfo()