feat(home): 장르 크리에이터 섹션을 연결한다

This commit is contained in:
Yu Sung
2026-06-29 20:47:46 +09:00
parent 561e991050
commit 2727cdc7cb
8 changed files with 225 additions and 12 deletions

View File

@@ -0,0 +1,147 @@
import SwiftUI
struct MainHomeGenreCreatorSection: View {
let items: [HomeGenreCreatorGroupItem]
let isFollowAllCompleted: (String) -> Bool
let isFollowAllLoading: (String) -> Bool
let onTapCreator: (Int) -> Void
let onTapFollowAll: ([Int], String) -> Void
init(
items: [HomeGenreCreatorGroupItem],
isFollowAllCompleted: @escaping (String) -> Bool = { _ in false },
isFollowAllLoading: @escaping (String) -> Bool = { _ in false },
onTapCreator: @escaping (Int) -> Void = { _ in },
onTapFollowAll: @escaping ([Int], String) -> Void = { _, _ in }
) {
self.items = items
self.isFollowAllCompleted = isFollowAllCompleted
self.isFollowAllLoading = isFollowAllLoading
self.onTapCreator = onTapCreator
self.onTapFollowAll = onTapFollowAll
}
var body: some View {
if !items.isEmpty {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .top, spacing: SodaSpacing.s4) {
ForEach(Array(items.enumerated()), id: \.offset) { index, item in
let followKey = "genre-\(index)-\(item.genreName)"
MainHomeCreatorGroupSection(
title: item.genreName,
creators: item.creators,
isFollowAllCompleted: isFollowAllCompleted(followKey),
isFollowAllLoading: isFollowAllLoading(followKey),
onTapCreator: onTapCreator,
onTapFollowAll: {
onTapFollowAll(item.creators.map(\.creatorId), followKey)
}
)
}
}
.padding(.leading, SodaSpacing.s14)
.padding(.trailing, SodaSpacing.s20)
}
}
}
}
struct MainHomeCreatorGroupSection: View {
let title: String
let creators: [HomeCreatorItem]
let isFollowAllCompleted: Bool
let isFollowAllLoading: Bool
let onTapCreator: (Int) -> Void
let onTapFollowAll: () -> Void
init(
title: String,
creators: [HomeCreatorItem],
isFollowAllCompleted: Bool = false,
isFollowAllLoading: Bool = false,
onTapCreator: @escaping (Int) -> Void = { _ in },
onTapFollowAll: @escaping () -> Void = {}
) {
self.title = title
self.creators = creators
self.isFollowAllCompleted = isFollowAllCompleted
self.isFollowAllLoading = isFollowAllLoading
self.onTapCreator = onTapCreator
self.onTapFollowAll = onTapFollowAll
}
var body: some View {
VStack(alignment: .leading, spacing: SodaSpacing.s14) {
Text(title)
.appFont(.heading3)
.foregroundColor(.white)
.lineLimit(1)
.truncationMode(.tail)
LazyVGrid(columns: columns, alignment: .center, spacing: SodaSpacing.s14) {
ForEach(creators.prefix(8), id: \.self) { creator in
CreatorProfileItem(
imageUrl: creator.creatorProfileImage,
name: creator.creatorNickname,
imageSize: CGSize(width: 75, height: 75),
spacing: SodaSpacing.s6
) {
onTapCreator(creator.creatorId)
}
.frame(width: 75)
}
}
FollowAllButton(
isCompleted: isFollowAllCompleted,
isLoading: isFollowAllLoading,
action: onTapFollowAll
)
}
.padding(SodaSpacing.s14)
.frame(width: 374)
.background(cardBackground)
.clipShape(RoundedRectangle(cornerRadius: SodaSpacing.s14, style: .continuous))
}
private var columns: [GridItem] {
Array(repeating: GridItem(.fixed(75), spacing: SodaSpacing.s14), count: 4)
}
private var cardBackground: some View {
LinearGradient(
stops: [
.init(color: Color.gray900, location: 0),
.init(color: Color.gray900, location: 0.29327),
.init(color: Color.soda700, location: 1)
],
startPoint: .top,
endPoint: .bottom
)
}
}
struct MainHomeGenreCreatorSection_Previews: PreviewProvider {
private static let previewImageUrl = "https://picsum.photos/200/200"
static var previews: some View {
MainHomeGenreCreatorSection(
items: [
HomeGenreCreatorGroupItem(
genreName: I18n.HomeRecommendation.genreCreatorSectionTitle,
creators: (1...8).map {
HomeCreatorItem(
creatorId: $0,
creatorNickname: "크리에이터이름",
creatorProfileImage: previewImageUrl
)
}
)
]
)
.padding(.vertical, SodaSpacing.s20)
.background(Color.black)
.previewLayout(.sizeThatFits)
}
}

View File

@@ -7,6 +7,7 @@ struct MainHomeRecommendationView: View {
let onTapCharacter: (Int) -> Void
let onTapCommunity: (Int) -> Void
let onTapBanner: (RecommendationBannerResponse) -> Void
let onTapFollowAll: (@escaping () -> Void) -> Void
@StateObject private var viewModel = MainHomeRecommendationViewModel()
@@ -47,6 +48,18 @@ struct MainHomeRecommendationView: View {
items: viewModel.recommendations?.aiCharacters ?? [],
onTapCharacter: onTapCharacter
)
MainHomeGenreCreatorSection(
items: viewModel.recommendations?.genreCreators ?? [],
isFollowAllCompleted: viewModel.isFollowAllCompleted,
isFollowAllLoading: viewModel.isFollowAllLoading,
onTapCreator: onTapCreator,
onTapFollowAll: { creatorIds, completionKey in
onTapFollowAll {
viewModel.followAll(creatorIds: creatorIds, completionKey: completionKey)
}
}
)
}
.padding(.vertical, SodaSpacing.s20)
}
@@ -73,7 +86,8 @@ struct MainHomeRecommendationView_Previews: PreviewProvider {
onTapContent: { _ in },
onTapCharacter: { _ in },
onTapCommunity: { _ in },
onTapBanner: { _ in }
onTapBanner: { _ in },
onTapFollowAll: { action in action() }
)
}
}