fix: 메인 홈 - 인기 크리에이터

- 팔로우 수 제거
- 팔로우 버튼 추가
- 배경: 그라데이션 제거, 하나의 색으로 설정
This commit is contained in:
Yu Sung
2025-07-21 23:10:24 +09:00
parent 23053b4223
commit 7318d7fcda
5 changed files with 94 additions and 40 deletions

View File

@@ -151,28 +151,28 @@ struct ContentMainTabHomeRankCreatorView: View {
nickname: "User1", nickname: "User1",
tags: "", tags: "",
profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
followerCount: 1000 follow: false
), ),
GetExplorerSectionCreatorResponse( GetExplorerSectionCreatorResponse(
id: 2, id: 2,
nickname: "User2", nickname: "User2",
tags: "", tags: "",
profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
followerCount: 1000 follow: false
), ),
GetExplorerSectionCreatorResponse( GetExplorerSectionCreatorResponse(
id: 3, id: 3,
nickname: "User3", nickname: "User3",
tags: "", tags: "",
profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
followerCount: 1000 follow: false
), ),
GetExplorerSectionCreatorResponse( GetExplorerSectionCreatorResponse(
id: 4, id: 4,
nickname: "User4", nickname: "User4",
tags: "", tags: "",
profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
followerCount: 1000 follow: false
) )
] ]
) )

View File

@@ -24,6 +24,6 @@ struct GetExplorerSectionCreatorResponse: Decodable, Hashable {
let nickname: String let nickname: String
let tags: String let tags: String
let profileImageUrl: String let profileImageUrl: String
let followerCount: Int var follow: Bool
} }

View File

@@ -11,54 +11,55 @@ import Kingfisher
struct HomeCreatorRankingItemView: View { struct HomeCreatorRankingItemView: View {
let rank: Int let rank: Int
let item: GetExplorerSectionCreatorResponse @State var item: GetExplorerSectionCreatorResponse
let onClickFollow: (Int, Bool) -> Void
let crowns = ["rank_1", "rank_2", "rank_3"] let crowns = ["rank_1", "rank_2", "rank_3"]
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
var body: some View { var body: some View {
ZStack(alignment: .topLeading) { ZStack(alignment: .topLeading) {
VStack(spacing: 0) { VStack(spacing: 0) {
KFImage(URL(string: item.profileImageUrl)) KFImage(URL(string: item.profileImageUrl))
.cancelOnDisappear(true) .cancelOnDisappear(true)
.resizable() .resizable()
.frame(width: 70, height: 70) .frame(width: 84, height: 84)
.clipShape(Circle()) .clipShape(Circle())
Text(item.nickname) Text(item.nickname)
.font(.custom(Font.preRegular.rawValue, size: 18)) .font(.custom(Font.preRegular.rawValue, size: 16))
.foregroundColor(.white) .foregroundColor(.white)
.padding(.top, 8) .padding(.top, 20)
Text("팔로워") Spacer()
.font(.custom(Font.preBold.rawValue, size: 14))
.foregroundColor(Color(hex: "78909C"))
.padding(.top, 12)
Text("\(item.followerCount)") if item.id != UserDefaults.int(forKey: .userId) {
.font(.custom(Font.preRegular.rawValue, size: 14)) Text(item.follow ? "팔로잉" : "팔로우")
.foregroundColor(Color(hex: "78909C")) .font(.custom(Font.preRegular.rawValue, size: 14))
.padding(.top, 4) .padding(.vertical, 4)
.frame(maxWidth: .infinity)
.background(
item.follow ? Color(hex: "455a64") : Color.white
)
.cornerRadius(999)
.foregroundColor(
item.follow ? .white : Color(hex: "263238")
)
.onTapGesture {
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
item.follow = !item.follow
}
onClickFollow(item.id, item.follow)
}
}
} }
.frame(width: 133, height: 188) .padding(16)
.background( .frame(width: 144, height: 204)
LinearGradient( .background(Color(hex: "263238"))
gradient: Gradient(colors: [Color(hex: "5ACDE1"), Color(hex: "2A339D")]),
startPoint: .topLeading,
endPoint: .bottom
)
)
.cornerRadius(16) .cornerRadius(16)
.overlay {
RoundedRectangle(cornerRadius: 16)
.strokeBorder(
LinearGradient(
gradient: Gradient(colors: [Color(hex: "9AE2F6"), .white.opacity(0)]),
startPoint: .top,
endPoint: .bottom
),
lineWidth: 1
)
}
.padding(.top, 20) .padding(.top, 20)
if rank <= 3 { if rank <= 3 {
@@ -79,7 +80,8 @@ struct HomeCreatorRankingItemView: View {
nickname: "유빈ASMR", nickname: "유빈ASMR",
tags: "", tags: "",
profileImageUrl: "https://cf.sodalive.net/profile/34806/34806-profile-49db6b45-bb1e-4dc7-917e-1a614a853f5f-4232-1752158072656", profileImageUrl: "https://cf.sodalive.net/profile/34806/34806-profile-49db6b45-bb1e-4dc7-917e-1a614a853f5f-4232-1752158072656",
followerCount: 1000 follow: true
) ),
onClickFollow: { _, _ in }
) )
} }

View File

@@ -109,7 +109,22 @@ struct HomeTabView: View {
HStack(spacing: 16) { HStack(spacing: 16) {
ForEach(0..<viewModel.creatorRanking.count, id: \.self) { ForEach(0..<viewModel.creatorRanking.count, id: \.self) {
let item = viewModel.creatorRanking[$0] let item = viewModel.creatorRanking[$0]
HomeCreatorRankingItemView(rank: $0 + 1, item: item) HomeCreatorRankingItemView(
rank: $0 + 1,
item: item,
onClickFollow: { creatorId, follow in
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if follow {
viewModel.creatorFollow(creatorId: item.id, follow: true, notify: true)
} else {
viewModel.creatorFollow(creatorId: item.id, follow: false, notify: false)
}
} else {
AppState.shared
.setAppStep(step: .login)
}
}
)
.onTapGesture { .onTapGesture {
if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
AppState.shared AppState.shared

View File

@@ -14,6 +14,7 @@ enum SeriesPublishedDaysOfWeek: String, Encodable {
final class HomeTabViewModel: ObservableObject { final class HomeTabViewModel: ObservableObject {
private let repository = HomeTabRepository() private let repository = HomeTabRepository()
private let userRepository = UserRepository()
private var subscription = Set<AnyCancellable>() private var subscription = Set<AnyCancellable>()
@Published var errorMessage = "" @Published var errorMessage = ""
@@ -160,4 +161,40 @@ final class HomeTabViewModel: ObservableObject {
} }
.store(in: &subscription) .store(in: &subscription)
} }
func creatorFollow(creatorId: Int, follow: Bool = true, notify: Bool = true) {
isLoading = true
userRepository.creatorFollow(creatorId: creatorId, follow: follow, notify: notify)
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { [unowned self] response in
self.isLoading = false
let responseData = response.data
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
if !decoded.success {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
}
}
.store(in: &subscription)
}
} }