feat: 메인 홈

- 요일별 시리즈, 오디션 추가
This commit is contained in:
Yu Sung
2025-07-12 01:20:02 +09:00
parent 5a9b95c2bf
commit 6a9854bdd7
6 changed files with 459 additions and 50 deletions

View File

@@ -0,0 +1,116 @@
//
// HomeAuditionView.swift
// SodaLive
//
// Created by klaus on 7/12/25.
//
import SwiftUI
import Kingfisher
struct HomeAuditionView: View {
@State private var currentIndex = 0
@State private var timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
let items: [GetAuditionListItem]
var body: some View {
VStack(spacing: 16) {
TabView(selection: $currentIndex) {
ForEach(0..<items.count, id: \.self) { index in
let item = items[index]
if let url = item.imageUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
KFImage(URL(string: url))
.cancelOnDisappear(true)
.resizable()
.scaledToFill()
.frame(
width: screenSize().width - 48,
height: (screenSize().width - 48) * 530 / 1000,
alignment: .center
)
.tag(index)
.onTapGesture {
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .auditionDetail(auditionId: item.id))
} else {
AppState.shared.setAppStep(step: .login)
}
}
} else {
KFImage(URL(string: item.imageUrl))
.cancelOnDisappear(true)
.resizable()
.scaledToFill()
.frame(
width: screenSize().width - 48,
height: (screenSize().width - 48) * 530 / 1000,
alignment: .center
)
.tag(index)
.onTapGesture {
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared.setAppStep(step: .auditionDetail(auditionId: item.id))
} else {
AppState.shared.setAppStep(step: .login)
}
}
}
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.frame(
width: screenSize().width,
height: screenSize().width * 530 / 1000,
alignment: .center
)
HStack(spacing: 8) {
ForEach(0..<items.count, id: \.self) { index in
Capsule()
.foregroundColor(index == currentIndex ? .button : .gray77)
.frame(width: 10, height: 10)
.tag(index)
}
}
}
.onAppear {
timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()
}
.onDisappear {
timer.upstream.connect().cancel()
}
.onReceive(timer) { _ in
DispatchQueue.main.async {
withAnimation {
if currentIndex == items.count - 1 {
currentIndex = 0
} else {
currentIndex += 1
}
}
}
}
}
}
#Preview {
HomeAuditionView(
items: [
GetAuditionListItem(
id: 1,
title: "오디션 제목 테스트",
imageUrl: "https://test-cf.sodalive.net/audition/production/1/audition-2950c964-b460-4085-87e4-f76849c826ab-240-1735215756152",
isOff: false
),
GetAuditionListItem(
id: 3,
title: "스위치온",
imageUrl: "https://test-cf.sodalive.net/audition/production/3/audition-aa934579-c01a-4da2-89fd-cce70d51c612-4267-1735908116928",
isOff: false
)
]
)
}

View File

@@ -115,37 +115,21 @@ struct HomeTabView: View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 16) {
ForEach(0..<viewModel.originalAudioDramaList.count, id: \.self) {
SeriesItemView(item: viewModel.originalAudioDramaList[$0])
}
}
.padding(.horizontal, 24)
}
}
}
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) {
}
.padding(.horizontal, 24)
}
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 16) {
}
.padding(.horizontal, 24)
}
if !viewModel.auditionList.isEmpty {
HomeAuditionView(items: viewModel.auditionList)
}
DayOfWeekSeriesView(seriesList: viewModel.dayOfWeekSeriesList) {
viewModel.getDayOfWeekSeriesList(dayOfWeek: $0)
}
VStack(alignment: .leading, spacing: 16) {

View File

@@ -121,4 +121,43 @@ final class HomeTabViewModel: ObservableObject {
}
.store(in: &subscription)
}
func getDayOfWeekSeriesList(dayOfWeek: SeriesPublishedDaysOfWeek) {
isLoading = true
repository.getDayOfWeekSeriesList(dayOfWeek: dayOfWeek)
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { [unowned self] response in
let responseData = response.data
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponse<[SeriesListItem]>.self, from: responseData)
if let data = decoded.data, decoded.success {
self.dayOfWeekSeriesList = data
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
self.isLoading = false
}
.store(in: &subscription)
}
}