feat: 콘텐츠 메인 - 새로운 콘텐츠, 큐레이션, 채널별 인기 콘텐츠, 시리즈 상세, 크리에이터 채널 콘텐츠 리스트

- 포인트 사용 가능 여부 표시
This commit is contained in:
Yu Sung
2025-06-10 19:43:29 +09:00
parent 842da82faf
commit 86cf466838
13 changed files with 122 additions and 46 deletions

View File

@@ -51,6 +51,15 @@ struct ContentListItemView: View {
.background(Color(hex: "222222")) .background(Color(hex: "222222"))
.cornerRadius(2.6) .cornerRadius(2.6)
if item.isPointAvailable {
Text("포인트")
.font(.custom(Font.medium.rawValue, size: 8))
.foregroundColor(.white)
.padding(2.6)
.background(Color(hex: "7849bc"))
.cornerRadius(2.6)
}
if item.isPin { if item.isPin {
Image("ic_pin") Image("ic_pin")
.resizable() .resizable()
@@ -162,7 +171,8 @@ struct ContentListItemView_Previews: PreviewProvider {
isScheduledToOpen: true, isScheduledToOpen: true,
isRented: false, isRented: false,
isOwned: false, isOwned: false,
isSoldOut: true isSoldOut: true,
isPointAvailable: true
) )
) )
} }

View File

@@ -14,7 +14,7 @@ struct ContentMainItemView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
ZStack(alignment: .topLeading) { ZStack(alignment: .topTrailing) {
KFImage(URL(string: item.coverImageUrl)) KFImage(URL(string: item.coverImageUrl))
.cancelOnDisappear(true) .cancelOnDisappear(true)
.downsampling( .downsampling(
@@ -27,6 +27,12 @@ struct ContentMainItemView: View {
.scaledToFill() .scaledToFill()
.frame(width: 133.3, height: 133.3, alignment: .top) .frame(width: 133.3, height: 133.3, alignment: .top)
.cornerRadius(2.7) .cornerRadius(2.7)
if item.isPointAvailable {
Image("ic_point")
.padding(.top, 2.7)
.padding(.trailing, 2.7)
}
} }
Text(item.title) Text(item.title)
@@ -75,7 +81,8 @@ struct ContentMainItemView_Previews: PreviewProvider {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저2", creatorNickname: "유저2",
price: 10, price: 10,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: true
) )
) )
} }

View File

@@ -33,6 +33,7 @@ struct GetAudioContentRankingItem: Decodable {
let duration: String let duration: String
let creatorId: Int let creatorId: Int
let creatorNickname: String let creatorNickname: String
let isPointAvailable: Bool
let creatorProfileImageUrl: String let creatorProfileImageUrl: String
} }
@@ -51,6 +52,7 @@ struct GetAudioContentMainItem: Decodable {
let creatorNickname: String let creatorNickname: String
let price: Int let price: Int
let duration: String let duration: String
let isPointAvailable: Bool
} }
struct GetAudioContentCurationResponse: Decodable { struct GetAudioContentCurationResponse: Decodable {

View File

@@ -133,6 +133,7 @@ struct ContentMainTabRankContentView: View {
duration: "00:30:20", duration: "00:30:20",
creatorId: 1, creatorId: 1,
creatorNickname: "유저1", creatorNickname: "유저1",
isPointAvailable: false,
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
), ),
GetAudioContentRankingItem( GetAudioContentRankingItem(
@@ -144,6 +145,7 @@ struct ContentMainTabRankContentView: View {
duration: "00:30:20", duration: "00:30:20",
creatorId: 2, creatorId: 2,
creatorNickname: "유저2", creatorNickname: "유저2",
isPointAvailable: false,
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
), ),
GetAudioContentRankingItem( GetAudioContentRankingItem(
@@ -155,6 +157,7 @@ struct ContentMainTabRankContentView: View {
duration: "00:30:20", duration: "00:30:20",
creatorId: 3, creatorId: 3,
creatorNickname: "유저3", creatorNickname: "유저3",
isPointAvailable: true,
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
) )
] ]

View File

@@ -91,18 +91,28 @@ struct ContentMainTagCurationContentView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
ZStack(alignment: .bottom) { ZStack(alignment: .bottom) {
KFImage(URL(string: item.coverImageUrl)) ZStack(alignment: .topTrailing) {
.cancelOnDisappear(true) KFImage(URL(string: item.coverImageUrl))
.downsampling( .cancelOnDisappear(true)
size: CGSize( .downsampling(
width: itemWidth, size: CGSize(
height: itemWidth width: itemWidth,
height: itemWidth
)
) )
) .resizable()
.resizable() .scaledToFill()
.scaledToFill() .frame(width: itemWidth, height: itemWidth, alignment: .top)
.frame(width: itemWidth, height: itemWidth, alignment: .top) .cornerRadius(2.7)
.cornerRadius(2.7)
if item.isPointAvailable {
Image("ic_point")
.resizable()
.frame(width: 20, height: 20)
.padding(.top, 2.7)
.padding(.trailing, 2.7)
}
}
VStack(spacing: 0) { VStack(spacing: 0) {
Spacer() Spacer()
@@ -193,7 +203,8 @@ struct ContentMainTagCurationContentView: View {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저1", creatorNickname: "유저1",
price: 100, price: 100,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: true
), ),
GetAudioContentMainItem( GetAudioContentMainItem(
contentId: 2, contentId: 2,
@@ -203,7 +214,8 @@ struct ContentMainTagCurationContentView: View {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저2", creatorNickname: "유저2",
price: 0, price: 0,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: false
), ),
GetAudioContentMainItem( GetAudioContentMainItem(
contentId: 3, contentId: 3,
@@ -213,7 +225,8 @@ struct ContentMainTagCurationContentView: View {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저3", creatorNickname: "유저3",
price: 1000, price: 1000,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: false
), ),
GetAudioContentMainItem( GetAudioContentMainItem(
contentId: 4, contentId: 4,
@@ -223,7 +236,8 @@ struct ContentMainTagCurationContentView: View {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저3", creatorNickname: "유저3",
price: 50000, price: 50000,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: false
) )
], ],
selectTag: { _ in } selectTag: { _ in }

View File

@@ -57,13 +57,21 @@ struct ContentByChannelView: View {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
ZStack(alignment:.bottom) { ZStack(alignment:.bottom) {
GeometryReader { geometry in GeometryReader { geometry in
KFImage(URL(string: content.coverImageUrl)) ZStack(alignment: .topTrailing) {
.cancelOnDisappear(true) KFImage(URL(string: content.coverImageUrl))
.resizable() .cancelOnDisappear(true)
.scaledToFill() .resizable()
.frame(width: geometry.size.width, height: geometry.size.width) .scaledToFill()
.clipShape(RoundedRectangle(cornerRadius: 5.3)) .frame(width: geometry.size.width, height: geometry.size.width)
.clipped() .clipShape(RoundedRectangle(cornerRadius: 5.3))
.clipped()
if content.isPointAvailable {
Image("ic_point")
.padding(.top, 2.7)
.padding(.trailing, 2.7)
}
}
} }
.aspectRatio(1, contentMode: .fit) .aspectRatio(1, contentMode: .fit)
@@ -168,6 +176,7 @@ struct ContentByChannelView: View {
duration: "00:30:20", duration: "00:30:20",
creatorId: 1, creatorId: 1,
creatorNickname: "유저1", creatorNickname: "유저1",
isPointAvailable: true,
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
), ),
GetAudioContentRankingItem( GetAudioContentRankingItem(
@@ -179,6 +188,7 @@ struct ContentByChannelView: View {
duration: "00:30:20", duration: "00:30:20",
creatorId: 1, creatorId: 1,
creatorNickname: "유저1", creatorNickname: "유저1",
isPointAvailable: false,
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
), ),
GetAudioContentRankingItem( GetAudioContentRankingItem(
@@ -190,6 +200,7 @@ struct ContentByChannelView: View {
duration: "00:30:20", duration: "00:30:20",
creatorId: 1, creatorId: 1,
creatorNickname: "유저1", creatorNickname: "유저1",
isPointAvailable: false,
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
), ),
GetAudioContentRankingItem( GetAudioContentRankingItem(
@@ -201,6 +212,7 @@ struct ContentByChannelView: View {
duration: "00:30:20", duration: "00:30:20",
creatorId: 1, creatorId: 1,
creatorNickname: "유저1", creatorNickname: "유저1",
isPointAvailable: false,
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
) )
] ]

View File

@@ -58,7 +58,8 @@ struct ContentMainCurationItemViewV2: View {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저1", creatorNickname: "유저1",
price: 10, price: 10,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: true
), ),
GetAudioContentMainItem( GetAudioContentMainItem(
contentId: 2, contentId: 2,
@@ -68,7 +69,8 @@ struct ContentMainCurationItemViewV2: View {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저2", creatorNickname: "유저2",
price: 10, price: 10,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: false
), ),
GetAudioContentMainItem( GetAudioContentMainItem(
contentId: 3, contentId: 3,
@@ -78,7 +80,8 @@ struct ContentMainCurationItemViewV2: View {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저3", creatorNickname: "유저3",
price: 10, price: 10,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: false
) )
] ]
) )

View File

@@ -72,7 +72,8 @@ struct ContentMainNewContentViewV2: View {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저1", creatorNickname: "유저1",
price: 10, price: 10,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: false
), ),
GetAudioContentMainItem( GetAudioContentMainItem(
contentId: 2, contentId: 2,
@@ -82,7 +83,8 @@ struct ContentMainNewContentViewV2: View {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저2", creatorNickname: "유저2",
price: 10, price: 10,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: false
), ),
GetAudioContentMainItem( GetAudioContentMainItem(
contentId: 3, contentId: 3,
@@ -92,7 +94,8 @@ struct ContentMainNewContentViewV2: View {
creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저3", creatorNickname: "유저3",
price: 10, price: 10,
duration: "00:00:30" duration: "00:00:30",
isPointAvailable: false
) )
], ],
selectTheme: { _ in } selectTheme: { _ in }

View File

@@ -24,12 +24,23 @@ struct SeriesContentListItemView: View {
.cornerRadius(5.3) .cornerRadius(5.3)
VStack(alignment: .leading, spacing: 2.7) { VStack(alignment: .leading, spacing: 2.7) {
Text(item.duration) HStack(spacing: 8) {
.font(.custom(Font.medium.rawValue, size: 10)) Text(item.duration)
.foregroundColor(Color.gray77) .font(.custom(Font.medium.rawValue, size: 10))
.padding(2.7) .foregroundColor(Color.gray77)
.background(Color.gray22) .padding(2.7)
.cornerRadius(2.6) .background(Color.gray22)
.cornerRadius(2.6)
if item.isPointAvailable {
Text("포인트")
.font(.custom(Font.medium.rawValue, size: 8))
.foregroundColor(.white)
.padding(2.6)
.background(Color(hex: "7849bc"))
.cornerRadius(2.6)
}
}
Text(item.title) Text(item.title)
.font(.custom(Font.medium.rawValue, size: 12)) .font(.custom(Font.medium.rawValue, size: 12))
@@ -91,7 +102,8 @@ struct SeriesContentListItemView: View {
duration: "00:14:59", duration: "00:14:59",
price: 0, price: 0,
isRented: false, isRented: false,
isOwned: false isOwned: false,
isPointAvailable: true
) )
) )
} }
@@ -106,7 +118,8 @@ struct SeriesContentListItemView: View {
duration: "00:14:59", duration: "00:14:59",
price: 100, price: 100,
isRented: false, isRented: false,
isOwned: false isOwned: false,
isPointAvailable: true
) )
) )
} }
@@ -121,7 +134,8 @@ struct SeriesContentListItemView: View {
duration: "00:14:59", duration: "00:14:59",
price: 200, price: 200,
isRented: true, isRented: true,
isOwned: false isOwned: false,
isPointAvailable: true
) )
) )
} }
@@ -136,7 +150,8 @@ struct SeriesContentListItemView: View {
duration: "00:14:59", duration: "00:14:59",
price: 300, price: 300,
isRented: false, isRented: false,
isOwned: true isOwned: true,
isPointAvailable: true
) )
) )
} }

View File

@@ -21,4 +21,5 @@ struct GetSeriesContentListItem: Decodable {
let price: Int let price: Int
let isRented: Bool let isRented: Bool
let isOwned: Bool let isOwned: Bool
let isPointAvailable: Bool
} }

View File

@@ -72,7 +72,8 @@ struct SeriesDetailHomeView: View {
duration: "00:14:59", duration: "00:14:59",
price: 0, price: 0,
isRented: false, isRented: false,
isOwned: false isOwned: false,
isPointAvailable: true
), ),
GetSeriesContentListItem( GetSeriesContentListItem(
contentId: 2, contentId: 2,
@@ -82,7 +83,8 @@ struct SeriesDetailHomeView: View {
duration: "00:14:59", duration: "00:14:59",
price: 100, price: 100,
isRented: false, isRented: false,
isOwned: false isOwned: false,
isPointAvailable: true
), ),
GetSeriesContentListItem( GetSeriesContentListItem(
contentId: 3, contentId: 3,
@@ -92,7 +94,8 @@ struct SeriesDetailHomeView: View {
duration: "00:14:59", duration: "00:14:59",
price: 100, price: 100,
isRented: true, isRented: true,
isOwned: false isOwned: false,
isPointAvailable: true
), ),
GetSeriesContentListItem( GetSeriesContentListItem(
contentId: 4, contentId: 4,
@@ -102,7 +105,8 @@ struct SeriesDetailHomeView: View {
duration: "00:14:59", duration: "00:14:59",
price: 100, price: 100,
isRented: false, isRented: false,
isOwned: true isOwned: true,
isPointAvailable: true
) )
] ]
) )

View File

@@ -87,6 +87,7 @@ struct GetAudioContentListItem: Decodable {
let isRented: Bool let isRented: Bool
let isOwned: Bool let isOwned: Bool
let isSoldOut: Bool let isSoldOut: Bool
let isPointAvailable: Bool
} }
struct GetCreatorActivitySummary: Decodable { struct GetCreatorActivitySummary: Decodable {

View File

@@ -77,7 +77,8 @@ struct UserProfileContentView_Previews: PreviewProvider {
isScheduledToOpen: false, isScheduledToOpen: false,
isRented: false, isRented: false,
isOwned: false, isOwned: false,
isSoldOut: false isSoldOut: false,
isPointAvailable: true
) )
] ]
) )