검색 UI 추가
This commit is contained in:
		
							
								
								
									
										239
									
								
								SodaLive/Sources/Search/SearchUnifiedView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								SodaLive/Sources/Search/SearchUnifiedView.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,239 @@
 | 
			
		||||
//
 | 
			
		||||
//  SearchUnifiedView.swift
 | 
			
		||||
//  SodaLive
 | 
			
		||||
//
 | 
			
		||||
//  Created by klaus on 3/27/25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
import SwiftUI
 | 
			
		||||
import Kingfisher
 | 
			
		||||
 | 
			
		||||
struct SearchUnifiedView: View {
 | 
			
		||||
    
 | 
			
		||||
    let creatorList: [SearchResponseItem]
 | 
			
		||||
    let contentList: [SearchResponseItem]
 | 
			
		||||
    let searchList: [SearchResponseItem]
 | 
			
		||||
    
 | 
			
		||||
    let onTapMoreCreator: () -> Void
 | 
			
		||||
    let onTapMoreContent: () -> Void
 | 
			
		||||
    let onTapMoreSeries: () -> Void
 | 
			
		||||
    
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        ScrollView(.vertical, showsIndicators: false) {
 | 
			
		||||
            VStack(spacing: 30) {
 | 
			
		||||
                if !creatorList.isEmpty {
 | 
			
		||||
                    SearchUnifiedItemView(
 | 
			
		||||
                        title: "채널",
 | 
			
		||||
                        itemList: creatorList,
 | 
			
		||||
                        onTapMore: onTapMoreCreator
 | 
			
		||||
                    )
 | 
			
		||||
                    .frame(maxWidth: .infinity)
 | 
			
		||||
                    .padding(.horizontal, 13.3)
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                if !contentList.isEmpty {
 | 
			
		||||
                    SearchUnifiedItemView(
 | 
			
		||||
                        title: "콘텐츠",
 | 
			
		||||
                        itemList: contentList,
 | 
			
		||||
                        onTapMore: onTapMoreContent
 | 
			
		||||
                    )
 | 
			
		||||
                    .frame(maxWidth: .infinity)
 | 
			
		||||
                    .padding(.horizontal, 13.3)
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                if !searchList.isEmpty {
 | 
			
		||||
                    SearchUnifiedItemView(
 | 
			
		||||
                        title: "시리즈",
 | 
			
		||||
                        itemList: searchList,
 | 
			
		||||
                        onTapMore: onTapMoreSeries
 | 
			
		||||
                    )
 | 
			
		||||
                    .frame(maxWidth: .infinity)
 | 
			
		||||
                    .padding(.horizontal, 13.3)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct SearchUnifiedItemView: View {
 | 
			
		||||
    let title: String
 | 
			
		||||
    let itemList: [SearchResponseItem]
 | 
			
		||||
    let onTapMore: () -> Void
 | 
			
		||||
    
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        VStack(alignment: .leading, spacing: 13.3) {
 | 
			
		||||
            Text(title)
 | 
			
		||||
                .font(.custom(Font.bold.rawValue, size: 16))
 | 
			
		||||
                .foregroundColor(.grayee)
 | 
			
		||||
            
 | 
			
		||||
            ForEach(0..<itemList.count, id: \.self) {
 | 
			
		||||
                let item = itemList[$0]
 | 
			
		||||
                
 | 
			
		||||
                switch item.type {
 | 
			
		||||
                case .CREATOR:
 | 
			
		||||
                    SearchCreatorItemView(item: item)
 | 
			
		||||
                    
 | 
			
		||||
                case .CONTENT:
 | 
			
		||||
                    SearchContentItemView(item: item)
 | 
			
		||||
                    
 | 
			
		||||
                case .SERIES:
 | 
			
		||||
                    SearchSeriesItemView(item: item)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            Text("더보기 >")
 | 
			
		||||
                .font(.custom(Font.medium.rawValue, size: 13.3))
 | 
			
		||||
                .foregroundColor(.grayee)
 | 
			
		||||
                .padding(.vertical, 10)
 | 
			
		||||
                .frame(maxWidth: .infinity)
 | 
			
		||||
                .background(Color.gray33.opacity(0.7))
 | 
			
		||||
                .onTapGesture { onTapMore() }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct SearchCreatorItemView: View {
 | 
			
		||||
    let item: SearchResponseItem
 | 
			
		||||
    
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        NavigationLink {
 | 
			
		||||
            UserProfileView(userId: item.id)
 | 
			
		||||
        } label: {
 | 
			
		||||
            HStack(spacing: 13.3) {
 | 
			
		||||
                KFImage(URL(string: item.imageUrl))
 | 
			
		||||
                    .cancelOnDisappear(true)
 | 
			
		||||
                    .downsampling(size: CGSize(width: 60, height: 60))
 | 
			
		||||
                    .resizable()
 | 
			
		||||
                    .frame(width: 60, height: 60)
 | 
			
		||||
                    .clipShape(Circle())
 | 
			
		||||
                
 | 
			
		||||
                Text(item.nickname)
 | 
			
		||||
                    .font(.custom(Font.medium.rawValue, size: 13.3))
 | 
			
		||||
                    .foregroundColor(Color.grayee)
 | 
			
		||||
                
 | 
			
		||||
                Spacer()
 | 
			
		||||
            }
 | 
			
		||||
            .frame(maxWidth: .infinity)
 | 
			
		||||
            .contentShape(Rectangle())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct SearchContentItemView: View {
 | 
			
		||||
    let item: SearchResponseItem
 | 
			
		||||
    
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        NavigationLink {
 | 
			
		||||
            ContentDetailView(contentId: item.id)
 | 
			
		||||
        } label: {
 | 
			
		||||
            HStack(spacing: 13.3) {
 | 
			
		||||
                KFImage(URL(string: item.imageUrl))
 | 
			
		||||
                    .cancelOnDisappear(true)
 | 
			
		||||
                    .downsampling(size: CGSize(width: 60, height: 60))
 | 
			
		||||
                    .resizable()
 | 
			
		||||
                    .frame(width: 60, height: 60)
 | 
			
		||||
                    .cornerRadius(5.3)
 | 
			
		||||
                
 | 
			
		||||
                VStack(alignment: .leading, spacing: 6.7) {
 | 
			
		||||
                    Text(item.title)
 | 
			
		||||
                        .font(.custom(Font.medium.rawValue, size: 13.3))
 | 
			
		||||
                        .foregroundColor(Color.grayee)
 | 
			
		||||
                    
 | 
			
		||||
                    Text(item.nickname)
 | 
			
		||||
                        .font(.custom(Font.medium.rawValue, size: 10))
 | 
			
		||||
                        .foregroundColor(Color.gray77)
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                Spacer()
 | 
			
		||||
            }
 | 
			
		||||
            .frame(maxWidth: .infinity)
 | 
			
		||||
            .contentShape(Rectangle())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct SearchSeriesItemView: View {
 | 
			
		||||
    let item: SearchResponseItem
 | 
			
		||||
    
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        NavigationLink {
 | 
			
		||||
            SeriesDetailView(seriesId: item.id)
 | 
			
		||||
        } label: {
 | 
			
		||||
            HStack(spacing: 13.3) {
 | 
			
		||||
                KFImage(URL(string: item.imageUrl))
 | 
			
		||||
                    .cancelOnDisappear(true)
 | 
			
		||||
                    .downsampling(size: CGSize(width: 60, height: 85))
 | 
			
		||||
                    .resizable()
 | 
			
		||||
                    .scaledToFill()
 | 
			
		||||
                    .frame(width: 60, height: 85)
 | 
			
		||||
                    .clipped()
 | 
			
		||||
                    .cornerRadius(5.3)
 | 
			
		||||
                
 | 
			
		||||
                VStack(alignment: .leading, spacing: 6.7) {
 | 
			
		||||
                    Text(item.title)
 | 
			
		||||
                        .font(.custom(Font.medium.rawValue, size: 13.3))
 | 
			
		||||
                        .foregroundColor(Color.grayee)
 | 
			
		||||
                    
 | 
			
		||||
                    Text(item.nickname)
 | 
			
		||||
                        .font(.custom(Font.medium.rawValue, size: 10))
 | 
			
		||||
                        .foregroundColor(Color.gray77)
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                Spacer()
 | 
			
		||||
            }
 | 
			
		||||
            .frame(maxWidth: .infinity)
 | 
			
		||||
            .contentShape(Rectangle())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#Preview {
 | 
			
		||||
    SearchUnifiedView(
 | 
			
		||||
        creatorList: [
 | 
			
		||||
            SearchResponseItem(
 | 
			
		||||
                id: 1,
 | 
			
		||||
                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
			
		||||
                title: "Tester",
 | 
			
		||||
                nickname: "Tester",
 | 
			
		||||
                type: .CREATOR
 | 
			
		||||
            )
 | 
			
		||||
        ],
 | 
			
		||||
        contentList: [
 | 
			
		||||
            SearchResponseItem(
 | 
			
		||||
                id: 1,
 | 
			
		||||
                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
			
		||||
                title: "Title1",
 | 
			
		||||
                nickname: "Tester",
 | 
			
		||||
                type: .CONTENT
 | 
			
		||||
            ),
 | 
			
		||||
            
 | 
			
		||||
            SearchResponseItem(
 | 
			
		||||
                id: 2,
 | 
			
		||||
                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
			
		||||
                title: "Title2",
 | 
			
		||||
                nickname: "Tester2",
 | 
			
		||||
                type: .CONTENT
 | 
			
		||||
            )
 | 
			
		||||
        ],
 | 
			
		||||
        searchList: [
 | 
			
		||||
            SearchResponseItem(
 | 
			
		||||
                id: 1,
 | 
			
		||||
                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
			
		||||
                title: "Title1",
 | 
			
		||||
                nickname: "Tester",
 | 
			
		||||
                type: .SERIES
 | 
			
		||||
            ),
 | 
			
		||||
            
 | 
			
		||||
            SearchResponseItem(
 | 
			
		||||
                id: 2,
 | 
			
		||||
                imageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
 | 
			
		||||
                title: "Title2",
 | 
			
		||||
                nickname: "Tester2",
 | 
			
		||||
                type: .SERIES
 | 
			
		||||
            )],
 | 
			
		||||
        onTapMoreCreator: {},
 | 
			
		||||
        onTapMoreContent: {},
 | 
			
		||||
        onTapMoreSeries: {}
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user