diff --git a/SodaLive.xcodeproj/project.pbxproj b/SodaLive.xcodeproj/project.pbxproj index 3e2f1f65..436f7cd3 100644 --- a/SodaLive.xcodeproj/project.pbxproj +++ b/SodaLive.xcodeproj/project.pbxproj @@ -213,15 +213,6 @@ 401A3FB22FEEAFA000A5BBA3 /* MainHomeRankingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401A3FB62FEEAFA000A5BBA3 /* MainHomeRankingView.swift */; }; 401A3FB32FEEAFA000A5BBA3 /* MainHomeFollowingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401A3FB72FEEAFA000A5BBA3 /* MainHomeFollowingView.swift */; }; 401A3FBA2FEEAFA000A5BBA3 /* MainHomeRecommendationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76506E56D017236824FE9B8F /* MainHomeRecommendationViewModel.swift */; }; - 401B3F012FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0A2FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift */; }; - 401B3F022FEEF10000A5BBA3 /* MainHomeRankingApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0B2FEEF10000A5BBA3 /* MainHomeRankingApi.swift */; }; - 401B3F032FEEF10000A5BBA3 /* MainHomeRankingRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0C2FEEF10000A5BBA3 /* MainHomeRankingRepository.swift */; }; - 401B3F042FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0D2FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift */; }; - 401B3F052FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0A2FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift */; }; - 401B3F062FEEF10000A5BBA3 /* MainHomeRankingApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0B2FEEF10000A5BBA3 /* MainHomeRankingApi.swift */; }; - 401B3F072FEEF10000A5BBA3 /* MainHomeRankingRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0C2FEEF10000A5BBA3 /* MainHomeRankingRepository.swift */; }; - 401B3F082FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0D2FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift */; }; - 401B3F092FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0D2FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift */; }; 401A3FC22FEEB00100A5BBA3 /* MainHomeLiveSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401A3FC12FEEB00100A5BBA3 /* MainHomeLiveSection.swift */; }; 401A3FC32FEEB00100A5BBA3 /* MainHomeLiveSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401A3FC12FEEB00100A5BBA3 /* MainHomeLiveSection.swift */; }; 401A3FC42FEEB00100A5BBA3 /* MainHomeLiveSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401A3FC12FEEB00100A5BBA3 /* MainHomeLiveSection.swift */; }; @@ -246,6 +237,15 @@ 401A3FE02FEEB00900A5BBA3 /* MainHomeBusinessInfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401A3FDF2FEEB00900A5BBA3 /* MainHomeBusinessInfoSection.swift */; }; 401A3FE12FEEB00900A5BBA3 /* MainHomeBusinessInfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401A3FDF2FEEB00900A5BBA3 /* MainHomeBusinessInfoSection.swift */; }; 401A3FE22FEEB00900A5BBA3 /* MainHomeBusinessInfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401A3FDF2FEEB00900A5BBA3 /* MainHomeBusinessInfoSection.swift */; }; + 401B3F012FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0A2FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift */; }; + 401B3F022FEEF10000A5BBA3 /* MainHomeRankingApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0B2FEEF10000A5BBA3 /* MainHomeRankingApi.swift */; }; + 401B3F032FEEF10000A5BBA3 /* MainHomeRankingRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0C2FEEF10000A5BBA3 /* MainHomeRankingRepository.swift */; }; + 401B3F042FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0D2FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift */; }; + 401B3F052FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0A2FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift */; }; + 401B3F062FEEF10000A5BBA3 /* MainHomeRankingApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0B2FEEF10000A5BBA3 /* MainHomeRankingApi.swift */; }; + 401B3F072FEEF10000A5BBA3 /* MainHomeRankingRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0C2FEEF10000A5BBA3 /* MainHomeRankingRepository.swift */; }; + 401B3F082FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0D2FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift */; }; + 401B3F092FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401B3F0D2FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift */; }; 40235DED2E3797EB00B09A87 /* RecentContent+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40235DEC2E3797EB00B09A87 /* RecentContent+CoreDataClass.swift */; }; 40235DEE2E3797EB00B09A87 /* RecentContent+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40235DEC2E3797EB00B09A87 /* RecentContent+CoreDataClass.swift */; }; 40235DF02E37983D00B09A87 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40235DEF2E37983D00B09A87 /* PersistenceController.swift */; }; @@ -282,6 +282,8 @@ 402C5F1B2CE24A6B00C07A3E /* LiveRoomHeartRankingItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402C5F192CE24A6B00C07A3E /* LiveRoomHeartRankingItemView.swift */; }; 402D60162BE273BC00766777 /* LiveRoomDonationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D60152BE273BC00766777 /* LiveRoomDonationResponse.swift */; }; 402D60172BE273BC00766777 /* LiveRoomDonationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402D60152BE273BC00766777 /* LiveRoomDonationResponse.swift */; }; + 402ECE152FF396E200B4FC34 /* MainHomeRankChangeBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402ECE132FF396E200B4FC34 /* MainHomeRankChangeBadge.swift */; }; + 402ECE162FF396E200B4FC34 /* MainHomeRankChangeBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402ECE132FF396E200B4FC34 /* MainHomeRankChangeBadge.swift */; }; 402FE16B2D50CD99004CFE30 /* AuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402FE16A2D50CD99004CFE30 /* AuthResponse.swift */; }; 402FE16C2D50CD99004CFE30 /* AuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402FE16A2D50CD99004CFE30 /* AuthResponse.swift */; }; 403085AA2D1188B700CB61DE /* GenerateUrlResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 403085A92D1188B700CB61DE /* GenerateUrlResponse.swift */; }; @@ -1759,10 +1761,6 @@ 401A3FB52FEEAFA000A5BBA3 /* MainHomeRecommendationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeRecommendationView.swift; sourceTree = ""; }; 401A3FB62FEEAFA000A5BBA3 /* MainHomeRankingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeRankingView.swift; sourceTree = ""; }; 401A3FB72FEEAFA000A5BBA3 /* MainHomeFollowingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeFollowingView.swift; sourceTree = ""; }; - 401B3F0A2FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeCreatorRankingResponse.swift; sourceTree = ""; }; - 401B3F0B2FEEF10000A5BBA3 /* MainHomeRankingApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeRankingApi.swift; sourceTree = ""; }; - 401B3F0C2FEEF10000A5BBA3 /* MainHomeRankingRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeRankingRepository.swift; sourceTree = ""; }; - 401B3F0D2FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeRankingViewModel.swift; sourceTree = ""; }; 401A3FC12FEEB00100A5BBA3 /* MainHomeLiveSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeLiveSection.swift; sourceTree = ""; }; 401A3FC52FEEB00200A5BBA3 /* MainHomeBannerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeBannerSection.swift; sourceTree = ""; }; 401A3FCC2FEEB00300A5BBA3 /* MainHomeActiveCreatorSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeActiveCreatorSection.swift; sourceTree = ""; }; @@ -1772,6 +1770,10 @@ 401A3FDB2FEEB00700A5BBA3 /* MainHomeCheerCreatorSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeCheerCreatorSection.swift; sourceTree = ""; }; 401A3FDC2FEEB00800A5BBA3 /* MainHomePopularCommunitySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomePopularCommunitySection.swift; sourceTree = ""; }; 401A3FDF2FEEB00900A5BBA3 /* MainHomeBusinessInfoSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeBusinessInfoSection.swift; sourceTree = ""; }; + 401B3F0A2FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeCreatorRankingResponse.swift; sourceTree = ""; }; + 401B3F0B2FEEF10000A5BBA3 /* MainHomeRankingApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeRankingApi.swift; sourceTree = ""; }; + 401B3F0C2FEEF10000A5BBA3 /* MainHomeRankingRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeRankingRepository.swift; sourceTree = ""; }; + 401B3F0D2FEEF10000A5BBA3 /* MainHomeRankingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeRankingViewModel.swift; sourceTree = ""; }; 40235DEC2E3797EB00B09A87 /* RecentContent+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RecentContent+CoreDataClass.swift"; sourceTree = ""; }; 40235DEF2E37983D00B09A87 /* PersistenceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistenceController.swift; sourceTree = ""; }; 40235DF22E37987300B09A87 /* RecentContentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentContentService.swift; sourceTree = ""; }; @@ -1790,6 +1792,7 @@ 402C5F162CE247CB00C07A3E /* LiveRoomHeartRankingDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveRoomHeartRankingDialog.swift; sourceTree = ""; }; 402C5F192CE24A6B00C07A3E /* LiveRoomHeartRankingItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveRoomHeartRankingItemView.swift; sourceTree = ""; }; 402D60152BE273BC00766777 /* LiveRoomDonationResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveRoomDonationResponse.swift; sourceTree = ""; }; + 402ECE132FF396E200B4FC34 /* MainHomeRankChangeBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeRankChangeBadge.swift; sourceTree = ""; }; 402FE16A2D50CD99004CFE30 /* AuthResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthResponse.swift; sourceTree = ""; }; 403085A92D1188B700CB61DE /* GenerateUrlResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerateUrlResponse.swift; sourceTree = ""; }; 4033CC6B2D2D096B00412C24 /* AuditionApplicantRecordingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuditionApplicantRecordingView.swift; sourceTree = ""; }; @@ -2755,6 +2758,7 @@ 401A3FB82FEEAFA000A5BBA3 /* Ranking */ = { isa = PBXGroup; children = ( + 402ECE142FF396E200B4FC34 /* Components */, 401B3F0E2FEEF10000A5BBA3 /* Models */, 401B3F0F2FEEF10000A5BBA3 /* Repository */, 401A3FB62FEEAFA000A5BBA3 /* MainHomeRankingView.swift */, @@ -2763,23 +2767,6 @@ path = Ranking; sourceTree = ""; }; - 401B3F0E2FEEF10000A5BBA3 /* Models */ = { - isa = PBXGroup; - children = ( - 401B3F0A2FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift */, - ); - path = Models; - sourceTree = ""; - }; - 401B3F0F2FEEF10000A5BBA3 /* Repository */ = { - isa = PBXGroup; - children = ( - 401B3F0B2FEEF10000A5BBA3 /* MainHomeRankingApi.swift */, - 401B3F0C2FEEF10000A5BBA3 /* MainHomeRankingRepository.swift */, - ); - path = Repository; - sourceTree = ""; - }; 401A3FB92FEEAFA000A5BBA3 /* Following */ = { isa = PBXGroup; children = ( @@ -2804,6 +2791,23 @@ path = Components; sourceTree = ""; }; + 401B3F0E2FEEF10000A5BBA3 /* Models */ = { + isa = PBXGroup; + children = ( + 401B3F0A2FEEF10000A5BBA3 /* MainHomeCreatorRankingResponse.swift */, + ); + path = Models; + sourceTree = ""; + }; + 401B3F0F2FEEF10000A5BBA3 /* Repository */ = { + isa = PBXGroup; + children = ( + 401B3F0B2FEEF10000A5BBA3 /* MainHomeRankingApi.swift */, + 401B3F0C2FEEF10000A5BBA3 /* MainHomeRankingRepository.swift */, + ); + path = Repository; + sourceTree = ""; + }; 40235DEA2E3797B000B09A87 /* Recent */ = { isa = PBXGroup; children = ( @@ -2843,6 +2847,14 @@ path = Temp; sourceTree = ""; }; + 402ECE142FF396E200B4FC34 /* Components */ = { + isa = PBXGroup; + children = ( + 402ECE132FF396E200B4FC34 /* MainHomeRankChangeBadge.swift */, + ); + path = Components; + sourceTree = ""; + }; 4033CC712D2D37B200412C24 /* Vote */ = { isa = PBXGroup; children = ( @@ -5961,6 +5973,7 @@ 406B61E12E66DC88005DEE6B /* UserMessageItemView.swift in Sources */, 40B4DF902D2A884F008B6FCF /* AuditionViewModel.swift in Sources */, 409143372A85665100EEBFB1 /* CanChargeViewModel.swift in Sources */, + 402ECE152FF396E200B4FC34 /* MainHomeRankChangeBadge.swift in Sources */, 4091444C2A861D8F00EEBFB1 /* UserProfileFanTalkView.swift in Sources */, 4091431B2A85604100EEBFB1 /* PaymentGateway.swift in Sources */, 40B28A522A837BAD008BE463 /* RoundedCorner.swift in Sources */, @@ -6579,6 +6592,7 @@ 40ED90A62E6189580097BB4E /* CharacterItemView.swift in Sources */, 40B640682E20B60300D0E4E4 /* RecommendChannelResponse.swift in Sources */, 400EA7BA2A8A6166004992CF /* LiveRoomTopCreatorView.swift in Sources */, + 402ECE162FF396E200B4FC34 /* MainHomeRankChangeBadge.swift in Sources */, 406A11C92A97202000D3BD2B /* OrderListAllViewModel.swift in Sources */, 409144472A85E6AF00EEBFB1 /* UserProfileDonationView.swift in Sources */, 40FFB2892E78512A004A0323 /* OriginalWorkDetailViewModel.swift in Sources */, diff --git a/SodaLive/Sources/V2/Main/Home/Ranking/Components/MainHomeRankChangeBadge.swift b/SodaLive/Sources/V2/Main/Home/Ranking/Components/MainHomeRankChangeBadge.swift new file mode 100644 index 00000000..3e04ea8a --- /dev/null +++ b/SodaLive/Sources/V2/Main/Home/Ranking/Components/MainHomeRankChangeBadge.swift @@ -0,0 +1,64 @@ +import SwiftUI + +struct MainHomeRankChangeBadge: View { + let showRankChange: Bool + let rankChange: Int? + let isNew: Bool + + var body: some View { + if showRankChange { + if isNew { + Image("ic_rank_new") + .resizable() + .scaledToFit() + .frame(width: 28, height: 12) + } else if let rankChange { + HStack(spacing: 2) { + switch rankChange { + case let value where value > 0: + rankChangeText(abs(value)) + rankChangeIcon("ic_rank_caret_increase") + case let value where value < 0: + rankChangeText(abs(value)) + rankChangeIcon("ic_rank_caret_decrease") + default: + rankChangeIcon("ic_rank_caret_stay") + } + } + .padding(.horizontal, 4) + .background(Color.gray900) + .cornerRadius(4) + } + } + } + + private func rankChangeText(_ value: Int) -> some View { + Text("\(value)") + .appFont(.caption2) + .foregroundColor(Color.gray400) + .lineLimit(1) + } + + private func rankChangeIcon(_ name: String) -> some View { + Image(name) + .resizable() + .scaledToFit() + .frame(width: 8, height: 8) + } +} + +struct MainHomeRankChangeBadge_Previews: PreviewProvider { + static var previews: some View { + VStack(alignment: .leading, spacing: SodaSpacing.s12) { + MainHomeRankChangeBadge(showRankChange: true, rankChange: 5, isNew: false) + MainHomeRankChangeBadge(showRankChange: true, rankChange: -3, isNew: false) + MainHomeRankChangeBadge(showRankChange: true, rankChange: 0, isNew: false) + MainHomeRankChangeBadge(showRankChange: true, rankChange: nil, isNew: true) + MainHomeRankChangeBadge(showRankChange: false, rankChange: 5, isNew: false) + MainHomeRankChangeBadge(showRankChange: true, rankChange: nil, isNew: false) + } + .padding(SodaSpacing.s20) + .background(Color.black) + .previewLayout(.sizeThatFits) + } +} diff --git a/docs/20260630_메인_홈_랭킹_탭/plan-task.md b/docs/20260630_메인_홈_랭킹_탭/plan-task.md index ceab25db..5d274c46 100644 --- a/docs/20260630_메인_홈_랭킹_탭/plan-task.md +++ b/docs/20260630_메인_홈_랭킹_탭/plan-task.md @@ -156,7 +156,7 @@ ### Phase 4: 순위 변화 UI 컴포넌트 -- [ ] **Task 4.1: 순위 변화 badge 컴포넌트 생성** +- [x] **Task 4.1: 순위 변화 badge 컴포넌트 생성** - 대상 파일: - 생성: `SodaLive/Sources/V2/Main/Home/Ranking/Components/MainHomeRankChangeBadge.swift` - 작업 내용: @@ -167,10 +167,11 @@ - `rankChange < 0`이면 절댓값 숫자와 `Image("ic_rank_caret_decrease")`를 표시한다. - `rankChange == 0`이면 숫자 없이 `Image("ic_rank_caret_stay")`만 표시한다. - `rankChange == nil && isNew == false`이면 아무것도 표시하지 않는다. + - Figma 기준 rank 변화 badge는 컴포넌트 내부에서 `Color.gray900`(`#202020`) 배경, horizontal padding 4, corner radius 4를 적용한다. - Preview에는 상승, 하락, 변동 없음, 신규 진입, `showRankChange == false`, `rankChange == nil && isNew == false` 상태를 모두 배치해 한 화면에서 UI 차이를 확인할 수 있게 한다. - 검증 기준: - - 실행 명령: `rg "struct MainHomeRankChangeBadge|ic_rank_new|ic_rank_caret_increase|ic_rank_caret_decrease|ic_rank_caret_stay|abs\\(|PreviewProvider|showRankChange: false|rankChange: nil|isNew: true|rankChange: 0" SodaLive/Sources/V2/Main/Home/Ranking/Components/MainHomeRankChangeBadge.swift` - - 기대 결과: PRD의 순위 변화 표시 규칙, 아이콘명, 조건별 Preview가 모두 확인된다. + - 실행 명령: `rg "struct MainHomeRankChangeBadge|ic_rank_new|ic_rank_caret_increase|ic_rank_caret_decrease|ic_rank_caret_stay|abs\\(|Color.gray900|cornerRadius\\(4\\)|padding\\(\\.horizontal, 4\\)|PreviewProvider|showRankChange: false|rankChange: nil|isNew: true|rankChange: 0" SodaLive/Sources/V2/Main/Home/Ranking/Components/MainHomeRankChangeBadge.swift` + - 기대 결과: PRD의 순위 변화 표시 규칙, Figma badge 배경/라운드/패딩, 아이콘명, 조건별 Preview가 모두 확인된다. ### Phase 5: 순위별 랭킹 UI 컴포넌트 @@ -348,3 +349,13 @@ - `I18n.HomeRanking.emptyStateMessage` 추가 - 검증 메모: - 현재 저장소 가이드상 테스트 번들 타깃이 확인되지 않아 신규 단위 테스트는 추가하지 않고 정적 검색 및 빌드 검증으로 확인 + +- 2026-06-30 Phase 4 진행: + - 목적: 랭킹 카드/row에서 재사용할 순위 변화 badge 컴포넌트 추가 + - 변경 내용: + - `MainHomeRankChangeBadge` 추가 + - `showRankChange`, `isNew`, `rankChange` 조건에 따라 신규/상승/하락/변동 없음/미표시 상태 분기 + - Phase 4 Preview에 상승, 하락, 변동 없음, 신규 진입, 순위 변화 숨김, `rankChange == nil && isNew == false` 상태 추가 + - 검증 메모: + - 문서의 Phase 4 정적 검색 기준으로 아이콘명, 조건별 Preview, `abs()` 사용을 확인 + - Figma badge 배경 기준 확인 후 `Color.gray900`(`#202020`) 배경, horizontal padding 4, corner radius 4를 `MainHomeRankChangeBadge`에 반영