feat: 메인 홈
- 주간 차트, 무료 콘텐츠 UI 추가
This commit is contained in:
		@@ -132,24 +132,8 @@ struct HomeTabView: View {
 | 
				
			|||||||
                                viewModel.getDayOfWeekSeriesList(dayOfWeek: $0)
 | 
					                                viewModel.getDayOfWeekSeriesList(dayOfWeek: $0)
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            
 | 
					                            
 | 
				
			||||||
                            VStack(alignment: .leading, spacing: 16) {
 | 
					                            if !viewModel.contentRanking.isEmpty {
 | 
				
			||||||
                                HStack(spacing: 0) {
 | 
					                                HomeWeeklyChartView(contentList: viewModel.contentRanking)
 | 
				
			||||||
                                    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)
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            
 | 
					                            
 | 
				
			||||||
                            VStack(alignment: .leading, spacing: 16) {
 | 
					                            VStack(alignment: .leading, spacing: 16) {
 | 
				
			||||||
@@ -172,23 +156,27 @@ struct HomeTabView: View {
 | 
				
			|||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            
 | 
					                            
 | 
				
			||||||
                            VStack(alignment: .leading, spacing: 16) {
 | 
					                            if !viewModel.freeContentList.isEmpty {
 | 
				
			||||||
                                HStack(spacing: 0) {
 | 
					                                VStack(alignment: .leading, spacing: 16) {
 | 
				
			||||||
                                    Text("무료")
 | 
					                                    HStack(spacing: 0) {
 | 
				
			||||||
                                        .font(.custom(Font.preBold.rawValue, size: 26))
 | 
					                                        Text("무료")
 | 
				
			||||||
                                        .foregroundColor(.button)
 | 
					                                            .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) {
 | 
					 | 
				
			||||||
                                        
 | 
					                                        
 | 
				
			||||||
 | 
					                                        Text(" 콘텐츠")
 | 
				
			||||||
 | 
					                                            .font(.custom(Font.preBold.rawValue, size: 26))
 | 
				
			||||||
 | 
					                                            .foregroundColor(.white)
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                    .padding(.horizontal, 24)
 | 
					                                    .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)
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            
 | 
					                            
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										63
									
								
								SodaLive/Sources/Home/HomeWeeklyChartItemView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								SodaLive/Sources/Home/HomeWeeklyChartItemView.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  HomeWeeklyChartItemView.swift
 | 
				
			||||||
 | 
					//  SodaLive
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by klaus on 7/12/25.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					import Kingfisher
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct HomeWeeklyChartItemView: View {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    let rank: Int
 | 
				
			||||||
 | 
					    let content: GetAudioContentRankingItem
 | 
				
			||||||
 | 
					    let onClickItem: (Int) -> Void
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    var body: some View {
 | 
				
			||||||
 | 
					        HStack(spacing: 16) {
 | 
				
			||||||
 | 
					            Text("\(rank)")
 | 
				
			||||||
 | 
					                .font(.custom(Font.preBold.rawValue, size: 16.7))
 | 
				
			||||||
 | 
					                .foregroundColor(Color(hex: "B5E7FA"))
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            KFImage(URL(string: content.coverImageUrl))
 | 
				
			||||||
 | 
					                .cancelOnDisappear(true)
 | 
				
			||||||
 | 
					                .resizable()
 | 
				
			||||||
 | 
					                .frame(width: 60, height: 60)
 | 
				
			||||||
 | 
					                .cornerRadius(12)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            VStack(alignment: .leading, spacing: 6) {
 | 
				
			||||||
 | 
					                Text(content.title)
 | 
				
			||||||
 | 
					                    .lineLimit(1)
 | 
				
			||||||
 | 
					                    .font(.custom(Font.preRegular.rawValue, size: 18))
 | 
				
			||||||
 | 
					                    .foregroundColor(.white)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                Text(content.creatorNickname)
 | 
				
			||||||
 | 
					                    .font(.custom(Font.preRegular.rawValue, size: 14))
 | 
				
			||||||
 | 
					                    .foregroundColor(Color(hex: "78909C"))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .frame(width: 242, alignment: .leading)
 | 
				
			||||||
 | 
					        .contentShape(Rectangle())
 | 
				
			||||||
 | 
					        .onTapGesture { onClickItem(content.contentId) }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Preview {
 | 
				
			||||||
 | 
					    HomeWeeklyChartItemView(
 | 
				
			||||||
 | 
					        rank: 1,
 | 
				
			||||||
 | 
					        content: GetAudioContentRankingItem(
 | 
				
			||||||
 | 
					            contentId: 1,
 | 
				
			||||||
 | 
					            title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....",
 | 
				
			||||||
 | 
					            coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
				
			||||||
 | 
					            themeStr: "커버곡",
 | 
				
			||||||
 | 
					            price: 100,
 | 
				
			||||||
 | 
					            duration: "00:30:20",
 | 
				
			||||||
 | 
					            creatorId: 1,
 | 
				
			||||||
 | 
					            creatorNickname: "유저1",
 | 
				
			||||||
 | 
					            isPointAvailable: false,
 | 
				
			||||||
 | 
					            creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        onClickItem: { _ in }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										149
									
								
								SodaLive/Sources/Home/HomeWeeklyChartView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								SodaLive/Sources/Home/HomeWeeklyChartView.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,149 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  HomeWeeklyChartView.swift
 | 
				
			||||||
 | 
					//  SodaLive
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by klaus on 7/12/25.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct HomeWeeklyChartView: View {
 | 
				
			||||||
 | 
					    @AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    let rows = [
 | 
				
			||||||
 | 
					        GridItem(.fixed(60), alignment: .leading),
 | 
				
			||||||
 | 
					        GridItem(.fixed(60), alignment: .leading),
 | 
				
			||||||
 | 
					        GridItem(.fixed(60), alignment: .leading),
 | 
				
			||||||
 | 
					        GridItem(.fixed(60), alignment: .leading)
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    let contentList: [GetAudioContentRankingItem]
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    var body: some View {
 | 
				
			||||||
 | 
					        VStack(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)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                Spacer()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            .padding(.horizontal, 24)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            ScrollView(.horizontal, showsIndicators: false) {
 | 
				
			||||||
 | 
					                LazyHGrid(rows: rows, spacing: 13.3) {
 | 
				
			||||||
 | 
					                    ForEach(0..<contentList.count, id: \.self) { index in
 | 
				
			||||||
 | 
					                        HomeWeeklyChartItemView(
 | 
				
			||||||
 | 
					                            rank: index + 1,
 | 
				
			||||||
 | 
					                            content: contentList[index]
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
 | 
					                            if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
 | 
				
			||||||
 | 
					                                AppState.shared
 | 
				
			||||||
 | 
					                                    .setAppStep(step: .contentDetail(contentId: $0))
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                AppState.shared
 | 
				
			||||||
 | 
					                                    .setAppStep(step: .login)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                .padding(.horizontal, 24)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Preview {
 | 
				
			||||||
 | 
					    HomeWeeklyChartView(
 | 
				
			||||||
 | 
					        contentList: [
 | 
				
			||||||
 | 
					            GetAudioContentRankingItem(
 | 
				
			||||||
 | 
					                contentId: 1,
 | 
				
			||||||
 | 
					                title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....",
 | 
				
			||||||
 | 
					                coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
				
			||||||
 | 
					                themeStr: "커버곡",
 | 
				
			||||||
 | 
					                price: 100,
 | 
				
			||||||
 | 
					                duration: "00:30:20",
 | 
				
			||||||
 | 
					                creatorId: 1,
 | 
				
			||||||
 | 
					                creatorNickname: "유저1",
 | 
				
			||||||
 | 
					                isPointAvailable: false,
 | 
				
			||||||
 | 
					                creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            GetAudioContentRankingItem(
 | 
				
			||||||
 | 
					                contentId: 2,
 | 
				
			||||||
 | 
					                title: "안녕하세요 오늘은 커버곡을 들려드릴께요....",
 | 
				
			||||||
 | 
					                coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
				
			||||||
 | 
					                themeStr: "커버곡",
 | 
				
			||||||
 | 
					                price: 0,
 | 
				
			||||||
 | 
					                duration: "00:30:20",
 | 
				
			||||||
 | 
					                creatorId: 2,
 | 
				
			||||||
 | 
					                creatorNickname: "유저2",
 | 
				
			||||||
 | 
					                isPointAvailable: false,
 | 
				
			||||||
 | 
					                creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            GetAudioContentRankingItem(
 | 
				
			||||||
 | 
					                contentId: 3,
 | 
				
			||||||
 | 
					                title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....",
 | 
				
			||||||
 | 
					                coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
				
			||||||
 | 
					                themeStr: "커버곡",
 | 
				
			||||||
 | 
					                price: 50,
 | 
				
			||||||
 | 
					                duration: "00:30:20",
 | 
				
			||||||
 | 
					                creatorId: 3,
 | 
				
			||||||
 | 
					                creatorNickname: "유저3",
 | 
				
			||||||
 | 
					                isPointAvailable: true,
 | 
				
			||||||
 | 
					                creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            GetAudioContentRankingItem(
 | 
				
			||||||
 | 
					                contentId: 3,
 | 
				
			||||||
 | 
					                title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....",
 | 
				
			||||||
 | 
					                coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
				
			||||||
 | 
					                themeStr: "커버곡",
 | 
				
			||||||
 | 
					                price: 50,
 | 
				
			||||||
 | 
					                duration: "00:30:20",
 | 
				
			||||||
 | 
					                creatorId: 3,
 | 
				
			||||||
 | 
					                creatorNickname: "유저3",
 | 
				
			||||||
 | 
					                isPointAvailable: true,
 | 
				
			||||||
 | 
					                creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            GetAudioContentRankingItem(
 | 
				
			||||||
 | 
					                contentId: 3,
 | 
				
			||||||
 | 
					                title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....",
 | 
				
			||||||
 | 
					                coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
				
			||||||
 | 
					                themeStr: "커버곡",
 | 
				
			||||||
 | 
					                price: 50,
 | 
				
			||||||
 | 
					                duration: "00:30:20",
 | 
				
			||||||
 | 
					                creatorId: 3,
 | 
				
			||||||
 | 
					                creatorNickname: "유저3",
 | 
				
			||||||
 | 
					                isPointAvailable: true,
 | 
				
			||||||
 | 
					                creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            GetAudioContentRankingItem(
 | 
				
			||||||
 | 
					                contentId: 3,
 | 
				
			||||||
 | 
					                title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....",
 | 
				
			||||||
 | 
					                coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
				
			||||||
 | 
					                themeStr: "커버곡",
 | 
				
			||||||
 | 
					                price: 50,
 | 
				
			||||||
 | 
					                duration: "00:30:20",
 | 
				
			||||||
 | 
					                creatorId: 3,
 | 
				
			||||||
 | 
					                creatorNickname: "유저3",
 | 
				
			||||||
 | 
					                isPointAvailable: true,
 | 
				
			||||||
 | 
					                creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            GetAudioContentRankingItem(
 | 
				
			||||||
 | 
					                contentId: 3,
 | 
				
			||||||
 | 
					                title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....",
 | 
				
			||||||
 | 
					                coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
				
			||||||
 | 
					                themeStr: "커버곡",
 | 
				
			||||||
 | 
					                price: 50,
 | 
				
			||||||
 | 
					                duration: "00:30:20",
 | 
				
			||||||
 | 
					                creatorId: 3,
 | 
				
			||||||
 | 
					                creatorNickname: "유저3",
 | 
				
			||||||
 | 
					                isPointAvailable: true,
 | 
				
			||||||
 | 
					                creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user