Files
sodalive-ios/SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift

211 lines
8.0 KiB
Swift

import SwiftUI
struct MainHomeFollowingNewsSection: View {
let recentNews: [FollowingNewsResponse]
let onTapCreator: (Int) -> Void
let onTapContent: (Int) -> Void
let onTapCommunityPost: (Int) -> Void
init(
recentNews: [FollowingNewsResponse],
onTapCreator: @escaping (Int) -> Void = { _ in },
onTapContent: @escaping (Int) -> Void = { _ in },
onTapCommunityPost: @escaping (Int) -> Void = { _ in }
) {
self.recentNews = recentNews
self.onTapCreator = onTapCreator
self.onTapContent = onTapContent
self.onTapCommunityPost = onTapCommunityPost
}
var body: some View {
if !renderableNews.isEmpty {
VStack(alignment: .leading, spacing: SodaSpacing.s14) {
SectionTitle(title: I18n.HomeFollowing.recentNewsTitle)
VStack(alignment: .leading, spacing: SodaSpacing.s8) {
ForEach(renderableNews) { news in
newsItemView(news)
}
}
.padding(.horizontal, SodaSpacing.s14)
}
}
}
private var renderableNews: [FollowingNewsResponse] {
recentNews.filter { news in
switch news.type {
case .creatorRanking:
return news.creatorRanking != nil
case .contentRanking:
return news.contentRanking != nil
case .communityPost:
return news.communityPost != nil
case .audioContent:
return news.audioContent != nil
case .photoContent:
return news.photoContent != nil
case .unknown:
return false
}
}
}
@ViewBuilder
private func newsItemView(_ news: FollowingNewsResponse) -> some View {
switch news.type {
case .creatorRanking:
if let creatorRanking = news.creatorRanking {
MainHomeFollowingRankingNewsCard(
creatorRanking: creatorRanking,
onTapCreator: onTapCreator
)
}
case .contentRanking:
if let contentRanking = news.contentRanking {
MainHomeFollowingRankingNewsCard(
contentRanking: contentRanking,
onTapContent: onTapContent
)
}
case .communityPost:
if let communityPost = news.communityPost {
CommunityPostCard(
postId: communityPost.postId,
creatorNickname: communityPost.creatorNickname,
creatorProfileImageUrl: communityPost.creatorProfileImage,
content: communityPost.content,
imageUrl: communityPost.imageUrl,
audioUrl: nil,
price: 0,
existOrdered: true,
createdAt: communityPost.relativeCreatedAtText(),
isLike: false,
likeCount: communityPost.likeCount,
commentCount: communityPost.commentCount,
onTapPost: {
onTapCommunityPost(communityPost.postId)
},
onTapComment: {
onTapCommunityPost(communityPost.postId)
}
)
}
case .audioContent:
if let audioContent = news.audioContent {
MainHomeFollowingContentNewsCard(
content: audioContent,
visibleFromAtUtc: news.visibleFromAtUtc,
variant: .audio,
onTapContent: onTapContent
)
}
case .photoContent:
if let photoContent = news.photoContent {
MainHomeFollowingContentNewsCard(
content: photoContent,
visibleFromAtUtc: news.visibleFromAtUtc,
variant: .photo,
onTapContent: onTapContent
)
}
case .unknown:
EmptyView()
}
}
}
private extension FollowingCommunityPostNewsResponse {
func relativeCreatedAtText(now: Date = Date()) -> String {
DateParser.relativeTimeText(fromUTC: createdAt, fallback: createdAt, now: now)
}
}
struct MainHomeFollowingNewsSection_Previews: PreviewProvider {
private static let previewImageUrl = "https://picsum.photos/500/500"
static var previews: some View {
ScrollView(.vertical) {
MainHomeFollowingNewsSection(
recentNews: [
FollowingNewsResponse(
newsId: "creator-ranking",
type: .creatorRanking,
visibleFromAtUtc: "2026-07-01T00:00:00Z",
creatorRanking: FollowingCreatorRankingNewsResponse(
rank: 2,
creatorId: 1,
nickname: "크리에이터",
profileImageUrl: previewImageUrl
),
audioContent: nil,
photoContent: nil,
contentRanking: nil,
communityPost: nil
),
FollowingNewsResponse(
newsId: "community-post",
type: .communityPost,
visibleFromAtUtc: "2026-07-01T00:00:00Z",
creatorRanking: nil,
audioContent: nil,
photoContent: nil,
contentRanking: nil,
communityPost: FollowingCommunityPostNewsResponse(
postId: 10,
creatorProfileImage: previewImageUrl,
creatorNickname: "크리에이터",
imageUrl: previewImageUrl,
content: "팔로잉 크리에이터의 커뮤니티 게시글입니다.",
createdAt: "2026-07-01T00:00:00Z",
likeCount: 5,
commentCount: 2
)
),
FollowingNewsResponse(
newsId: "audio-content",
type: .audioContent,
visibleFromAtUtc: "2026-07-01T00:02:00Z",
creatorRanking: nil,
audioContent: FollowingContentNewsResponse(
contentId: 20,
contentImageUrl: previewImageUrl,
title: "오디오 콘텐츠 이름",
creatorProfileImageUrl: previewImageUrl,
creatorNickname: "크리에이터이름"
),
photoContent: nil,
contentRanking: nil,
communityPost: nil
),
FollowingNewsResponse(
newsId: "photo-content",
type: .photoContent,
visibleFromAtUtc: "2026-07-01T00:03:00Z",
creatorRanking: nil,
audioContent: nil,
photoContent: FollowingContentNewsResponse(
contentId: 21,
contentImageUrl: previewImageUrl,
title: "화보 콘텐츠 이름",
creatorProfileImageUrl: previewImageUrl,
creatorNickname: "크리에이터이름"
),
contentRanking: nil,
communityPost: nil
)
]
)
.padding(.vertical, SodaSpacing.s20)
.background(Color.black)
.previewLayout(.sizeThatFits)
}
}
}