316 lines
17 KiB
Swift
316 lines
17 KiB
Swift
//
|
|
// HomeTabView.swift
|
|
// SodaLive
|
|
//
|
|
// Created by klaus on 7/10/25.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct HomeTabView: View {
|
|
@StateObject var viewModel = HomeTabViewModel()
|
|
@StateObject var liveViewModel = LiveViewModel()
|
|
|
|
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
|
|
@AppStorage("role") private var role: String = UserDefaults.string(forKey: UserDefaultsKey.role)
|
|
|
|
var body: some View {
|
|
BaseView(isLoading: $viewModel.isLoading) {
|
|
ZStack(alignment: .bottomTrailing) {
|
|
VStack(alignment: .leading, spacing: 0) {
|
|
HStack(spacing: 24) {
|
|
Image("img_text_logo")
|
|
|
|
Spacer()
|
|
|
|
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
Image("ic_search_white")
|
|
.onTapGesture {
|
|
AppState
|
|
.shared
|
|
.setAppStep(step: .search)
|
|
}
|
|
|
|
Image("ic_can_circle")
|
|
.onTapGesture {
|
|
AppState
|
|
.shared
|
|
.setAppStep(step: .canCharge(refresh: {}))
|
|
}
|
|
|
|
Image("ic_storage")
|
|
.onTapGesture {
|
|
AppState
|
|
.shared
|
|
.setAppStep(step: .myBox(currentTab: .orderlist))
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 24)
|
|
.padding(.vertical, 20)
|
|
|
|
ScrollView(.vertical, showsIndicators: false) {
|
|
VStack(alignment: .leading, spacing: 48) {
|
|
if !viewModel.liveList.isEmpty {
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HStack(spacing: 0) {
|
|
Text("지금")
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.button)
|
|
|
|
Text(" 라이브중")
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.white)
|
|
}
|
|
.padding(.horizontal, 24)
|
|
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 16) {
|
|
ForEach(0..<viewModel.liveList.count, id: \.self) { index in
|
|
HomeLiveItemView(item: viewModel.liveList[index]) { roomId in
|
|
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
AppState.shared.setAppStep(
|
|
step: .liveDetail(
|
|
roomId: roomId,
|
|
onClickParticipant: {
|
|
AppState.shared.isShowPlayer = false
|
|
liveViewModel.enterLiveRoom(roomId: roomId)
|
|
},
|
|
onClickReservation: {},
|
|
onClickStart: {},
|
|
onClickCancel: {}
|
|
)
|
|
)
|
|
} else {
|
|
AppState.shared.setAppStep(step: .login)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 24)
|
|
}
|
|
}
|
|
}
|
|
|
|
if !viewModel.creatorRanking.isEmpty {
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HStack(spacing: 0) {
|
|
Text("인기")
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.button)
|
|
|
|
Text(" 크리에이터")
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.white)
|
|
}
|
|
.padding(.horizontal, 24)
|
|
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 16) {
|
|
ForEach(0..<viewModel.creatorRanking.count, id: \.self) {
|
|
let item = viewModel.creatorRanking[$0]
|
|
HomeCreatorRankingItemView(rank: $0 + 1, item: item)
|
|
.onTapGesture {
|
|
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
AppState.shared
|
|
.setAppStep(step: .creatorDetail(userId: item.id))
|
|
} else {
|
|
AppState.shared
|
|
.setAppStep(step: .login)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 24)
|
|
}
|
|
}
|
|
}
|
|
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HomeLatestContentView(
|
|
onClickMore: {
|
|
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
AppState.shared
|
|
.setAppStep(step: .newContentAll(isFree: false))
|
|
} else {
|
|
AppState.shared
|
|
.setAppStep(step: .login)
|
|
}
|
|
},
|
|
themeList: viewModel.latestContentThemeList,
|
|
contentList: viewModel.latestContentList
|
|
) {
|
|
viewModel.getLatestContentByTheme(theme: $0)
|
|
}
|
|
}
|
|
|
|
if !viewModel.eventBannerList.isEmpty {
|
|
SectionEventBannerView(items: viewModel.eventBannerList)
|
|
}
|
|
|
|
if !viewModel.originalAudioDramaList.isEmpty {
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HStack(spacing: 0) {
|
|
Text("오직")
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.button)
|
|
|
|
Text(" 보이스온에서만")
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.white)
|
|
}
|
|
.padding(.horizontal, 24)
|
|
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 16) {
|
|
ForEach(0..<viewModel.originalAudioDramaList.count, id: \.self) {
|
|
SeriesItemView(item: viewModel.originalAudioDramaList[$0])
|
|
}
|
|
}
|
|
.padding(.horizontal, 24)
|
|
}
|
|
}
|
|
}
|
|
|
|
if !viewModel.auditionList.isEmpty {
|
|
HomeAuditionView(items: viewModel.auditionList)
|
|
}
|
|
|
|
DayOfWeekSeriesView(seriesList: viewModel.dayOfWeekSeriesList) {
|
|
viewModel.getDayOfWeekSeriesList(dayOfWeek: $0)
|
|
}
|
|
|
|
if !viewModel.contentRanking.isEmpty {
|
|
HomeWeeklyChartView(contentList: viewModel.contentRanking)
|
|
}
|
|
|
|
if !viewModel.recommendChannelList.isEmpty {
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HStack(spacing: 0) {
|
|
Text("추천")
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.button)
|
|
|
|
Text(" 채널")
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.white)
|
|
}
|
|
.padding(.horizontal, 24)
|
|
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 16) {
|
|
ForEach(0..<viewModel.recommendChannelList.count, id: \.self) {
|
|
RecommendChannelItemView(item: viewModel.recommendChannelList[$0])
|
|
}
|
|
}
|
|
.padding(.horizontal, 24)
|
|
}
|
|
}
|
|
}
|
|
|
|
if !viewModel.freeContentList.isEmpty {
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HStack(spacing: 0) {
|
|
Text("무료")
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.button)
|
|
|
|
Text(" 콘텐츠")
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.white)
|
|
}
|
|
.padding(.horizontal, 24)
|
|
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 16) {
|
|
ForEach(0..<viewModel.freeContentList.count, id: \.self) { index in
|
|
ContentItemView(item: viewModel.freeContentList[index])
|
|
}
|
|
}
|
|
.padding(.horizontal, 24)
|
|
}
|
|
}
|
|
}
|
|
|
|
if !viewModel.curationList.isEmpty {
|
|
ForEach(0..<viewModel.curationList.count, id: \.self) { curationIndex in
|
|
let curation = viewModel.curationList[curationIndex]
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HStack(spacing: 0) {
|
|
Text(curation.title)
|
|
.font(.custom(Font.preBold.rawValue, size: 26))
|
|
.foregroundColor(.white)
|
|
}
|
|
.padding(.horizontal, 24)
|
|
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 16) {
|
|
ForEach(0..<curation.items.count, id: \.self) { index in
|
|
let item = curation.items[index]
|
|
ContentItemView(
|
|
item: AudioContentMainItem(
|
|
contentId: item.contentId,
|
|
creatorId: item.creatorId,
|
|
title: item.title,
|
|
coverImageUrl: item.coverImageUrl,
|
|
creatorNickname: item.creatorNickname,
|
|
isPointAvailable: item.isPointAvailable
|
|
)
|
|
)
|
|
}
|
|
}
|
|
.padding(.horizontal, 24)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Text("""
|
|
- 회사명 : 주식회사 소다라이브
|
|
|
|
- 대표자 : 이재형
|
|
|
|
- 주소 : 경기도 성남시 분당구 황새울로335번길 10, 5층 563A호
|
|
|
|
- 사업자등록번호 : 870-81-03220
|
|
|
|
- 통신판매업신고 : 제2024-성남분당B-1012호
|
|
|
|
- 고객센터 : 02.2055.1477 (이용시간 10:00~19:00)
|
|
|
|
- 대표 이메일 : sodalive.official@gmail.com
|
|
""")
|
|
.font(.custom(Font.medium.rawValue, size: 11))
|
|
.foregroundColor(Color.gray77)
|
|
.padding(.horizontal, 13.3)
|
|
}
|
|
.padding(.vertical, 24)
|
|
}
|
|
}
|
|
}
|
|
.onAppear {
|
|
viewModel.fetchData()
|
|
}
|
|
}
|
|
.popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) {
|
|
HStack {
|
|
Spacer()
|
|
Text(viewModel.errorMessage)
|
|
.padding(.vertical, 13.3)
|
|
.frame(width: screenSize().width - 66.7, alignment: .center)
|
|
.font(.custom(Font.medium.rawValue, size: 12))
|
|
.background(Color.button)
|
|
.foregroundColor(Color.white)
|
|
.multilineTextAlignment(.leading)
|
|
.cornerRadius(20)
|
|
.padding(.bottom, 66.7)
|
|
Spacer()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
HomeTabView()
|
|
}
|