feat(home): 팔로잉 크리에이터 섹션을 추가한다
This commit is contained in:
@@ -258,6 +258,9 @@
|
||||
401C10322FEF300000A5BBA3 /* MainHomeFollowingApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C10022FEF300000A5BBA3 /* MainHomeFollowingApi.swift */; };
|
||||
401C10332FEF300000A5BBA3 /* MainHomeFollowingRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C10032FEF300000A5BBA3 /* MainHomeFollowingRepository.swift */; };
|
||||
401C10342FEF300000A5BBA3 /* MainHomeFollowingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C10042FEF300000A5BBA3 /* MainHomeFollowingViewModel.swift */; };
|
||||
401C10412FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C10442FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.swift */; };
|
||||
401C10422FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C10442FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.swift */; };
|
||||
401C10432FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C10442FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.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 */; };
|
||||
@@ -1796,6 +1799,7 @@
|
||||
401C10022FEF300000A5BBA3 /* MainHomeFollowingApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeFollowingApi.swift; sourceTree = "<group>"; };
|
||||
401C10032FEF300000A5BBA3 /* MainHomeFollowingRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeFollowingRepository.swift; sourceTree = "<group>"; };
|
||||
401C10042FEF300000A5BBA3 /* MainHomeFollowingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeFollowingViewModel.swift; sourceTree = "<group>"; };
|
||||
401C10442FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainHomeFollowingCreatorSection.swift; sourceTree = "<group>"; };
|
||||
40235DEC2E3797EB00B09A87 /* RecentContent+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RecentContent+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
40235DEF2E37983D00B09A87 /* PersistenceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistenceController.swift; sourceTree = "<group>"; };
|
||||
40235DF22E37987300B09A87 /* RecentContentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentContentService.swift; sourceTree = "<group>"; };
|
||||
@@ -2797,12 +2801,21 @@
|
||||
children = (
|
||||
401C10052FEF300000A5BBA3 /* Models */,
|
||||
401C10062FEF300000A5BBA3 /* Repository */,
|
||||
401C10402FEF500000A5BBA3 /* Components */,
|
||||
401C10042FEF300000A5BBA3 /* MainHomeFollowingViewModel.swift */,
|
||||
401A3FB72FEEAFA000A5BBA3 /* MainHomeFollowingView.swift */,
|
||||
);
|
||||
path = Following;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
401C10402FEF500000A5BBA3 /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
401C10442FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.swift */,
|
||||
);
|
||||
path = Components;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
401A3FC02FEEB00100A5BBA3 /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -5467,6 +5480,7 @@
|
||||
401C10122FEF300000A5BBA3 /* MainHomeFollowingApi.swift in Sources */,
|
||||
401C10132FEF300000A5BBA3 /* MainHomeFollowingRepository.swift in Sources */,
|
||||
401C10142FEF300000A5BBA3 /* MainHomeFollowingViewModel.swift in Sources */,
|
||||
401C10412FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.swift in Sources */,
|
||||
409143302A85652F00EEBFB1 /* CanChargeView.swift in Sources */,
|
||||
40975CA32B572298004E2F1B /* LiveRoomOverlayStrokeTextButton.swift in Sources */,
|
||||
40235DED2E3797EB00B09A87 /* RecentContent+CoreDataClass.swift in Sources */,
|
||||
@@ -6231,6 +6245,7 @@
|
||||
401C10222FEF300000A5BBA3 /* MainHomeFollowingApi.swift in Sources */,
|
||||
401C10232FEF300000A5BBA3 /* MainHomeFollowingRepository.swift in Sources */,
|
||||
401C10242FEF300000A5BBA3 /* MainHomeFollowingViewModel.swift in Sources */,
|
||||
401C10422FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.swift in Sources */,
|
||||
B3AF650899B8FC84E0C0CDA0 /* MainPlaceholderTabView.swift in Sources */,
|
||||
CA65AD0D677B4A8E9CAC0E15 /* MainTab.swift in Sources */,
|
||||
A0643C829BE7377DC09112CB /* MainTabBarButton.swift in Sources */,
|
||||
@@ -7025,6 +7040,7 @@
|
||||
401C10322FEF300000A5BBA3 /* MainHomeFollowingApi.swift in Sources */,
|
||||
401C10332FEF300000A5BBA3 /* MainHomeFollowingRepository.swift in Sources */,
|
||||
401C10342FEF300000A5BBA3 /* MainHomeFollowingViewModel.swift in Sources */,
|
||||
401C10432FEF500000A5BBA3 /* MainHomeFollowingCreatorSection.swift in Sources */,
|
||||
ACD71056E97E13E48941ABBB /* MainPlaceholderTabView.swift in Sources */,
|
||||
33BA3AB731849284E62AE04B /* MainTab.swift in Sources */,
|
||||
B11681AC02BF51C6E2FA6FF3 /* MainTabBarButton.swift in Sources */,
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import SwiftUI
|
||||
|
||||
struct MainHomeFollowingCreatorSection: View {
|
||||
let followingCreators: [FollowingCreatorResponse]
|
||||
let onTapCreator: (Int) -> Void
|
||||
let onTapAll: () -> Void
|
||||
|
||||
init(
|
||||
followingCreators: [FollowingCreatorResponse],
|
||||
onTapCreator: @escaping (Int) -> Void = { _ in },
|
||||
onTapAll: @escaping () -> Void = {}
|
||||
) {
|
||||
self.followingCreators = followingCreators
|
||||
self.onTapCreator = onTapCreator
|
||||
self.onTapAll = onTapAll
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack(alignment: .top, spacing: SodaSpacing.s12) {
|
||||
ForEach(followingCreators) { creator in
|
||||
CreatorProfileItem(
|
||||
imageUrl: creator.creatorProfileImageUrl,
|
||||
name: creator.creatorNickname,
|
||||
imageSize: CGSize(width: 72, height: 72),
|
||||
spacing: SodaSpacing.s8
|
||||
) {
|
||||
onTapCreator(creator.creatorId)
|
||||
}
|
||||
.frame(width: 72)
|
||||
}
|
||||
|
||||
Button(action: onTapAll) {
|
||||
Text(I18n.HomeFollowing.allButtonTitle)
|
||||
.appFont(.body4)
|
||||
.foregroundColor(Color.soda400)
|
||||
.padding(.horizontal, 16)
|
||||
.frame(height: allButtonHeight)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
.padding(.horizontal, SodaSpacing.s20)
|
||||
}
|
||||
}
|
||||
|
||||
private var allButtonHeight: CGFloat { 72 + SodaSpacing.s8 + 20 }
|
||||
}
|
||||
|
||||
struct MainHomeFollowingCreatorSection_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack(spacing: SodaSpacing.s20) {
|
||||
MainHomeFollowingCreatorSection(
|
||||
followingCreators: [
|
||||
FollowingCreatorResponse(
|
||||
creatorId: 1,
|
||||
creatorNickname: "크리에이터",
|
||||
creatorProfileImageUrl: "https://picsum.photos/200/200"
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
MainHomeFollowingCreatorSection(followingCreators: [])
|
||||
}
|
||||
.padding(.vertical, SodaSpacing.s20)
|
||||
.background(Color.black)
|
||||
.previewLayout(.sizeThatFits)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,22 @@
|
||||
import SwiftUI
|
||||
|
||||
struct MainHomeFollowingView: View {
|
||||
let onTapFollowingAll: () -> Void
|
||||
|
||||
init(onTapFollowingAll: @escaping () -> Void = {}) {
|
||||
self.onTapFollowingAll = onTapFollowingAll
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
MainHomeFollowingCreatorSection(
|
||||
followingCreators: [],
|
||||
onTapAll: onTapFollowingAll
|
||||
)
|
||||
|
||||
MainPlaceholderTabView(title: "팔로잉")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MainHomeFollowingView_Previews: PreviewProvider {
|
||||
|
||||
@@ -12,6 +12,7 @@ struct MainHomeView: View {
|
||||
let onTapBanner: (RecommendationBannerResponse) -> Void
|
||||
let onTapFollowAll: (@escaping () -> Void) -> Void
|
||||
let onSelectFollowingTab: () -> Bool
|
||||
let onTapFollowingAll: () -> Void
|
||||
|
||||
@State private var selectedTab: MainHomeTab = .recommendation
|
||||
|
||||
@@ -71,7 +72,7 @@ struct MainHomeView: View {
|
||||
case .ranking:
|
||||
MainHomeRankingView(onTapCreator: onTapCreator)
|
||||
case .following:
|
||||
MainHomeFollowingView()
|
||||
MainHomeFollowingView(onTapFollowingAll: onTapFollowingAll)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,7 +107,8 @@ struct MainHomeView_Previews: PreviewProvider {
|
||||
onTapCommunity: { _ in },
|
||||
onTapBanner: { _ in },
|
||||
onTapFollowAll: { action in action() },
|
||||
onSelectFollowingTab: { true }
|
||||
onSelectFollowingTab: { true },
|
||||
onTapFollowingAll: {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,8 @@ struct MainView: View {
|
||||
onTapCommunity: handleRecommendationCommunityTap,
|
||||
onTapBanner: handleRecommendationBannerTap,
|
||||
onTapFollowAll: handleRecommendationFollowAllTap,
|
||||
onSelectFollowingTab: handleFollowingTabSelection
|
||||
onSelectFollowingTab: handleFollowingTabSelection,
|
||||
onTapFollowingAll: handleFollowingAllTap
|
||||
)
|
||||
case .content:
|
||||
MainPlaceholderTabView(title: MainTab.content.title)
|
||||
@@ -485,6 +486,10 @@ struct MainView: View {
|
||||
performRecommendationDetailAction(action: action)
|
||||
}
|
||||
|
||||
private func handleFollowingAllTap() {
|
||||
appState.setAppStep(step: .followingList)
|
||||
}
|
||||
|
||||
private func handleFollowingTabSelection() -> Bool {
|
||||
let trimmed = token.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty else {
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
|
||||
### Phase 3: 팔로잉 크리에이터 섹션과 전체 버튼
|
||||
|
||||
- [ ] **Task 3.1: 팔로잉 크리에이터 섹션 생성**
|
||||
- [x] **Task 3.1: 팔로잉 크리에이터 섹션 생성**
|
||||
- 대상 파일:
|
||||
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingCreatorSection.swift`
|
||||
- 확인: `SodaLive/Sources/V2/Component/Creator/CreatorProfileItem.swift`
|
||||
@@ -193,7 +193,7 @@
|
||||
- 실행 명령: `rg "struct MainHomeFollowingCreatorSection|ScrollView\\(\\.horizontal|followingCreators|onTapCreator|onTapAll|allButtonTitle|Color.soda400|padding\\(\\.horizontal, 16\\)" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingCreatorSection.swift`
|
||||
- 기대 결과: 가로 creator 목록과 마지막 `전체` 버튼 요구사항이 확인된다.
|
||||
|
||||
- [ ] **Task 3.2: 전체 팔로잉 목록 진입 연결**
|
||||
- [x] **Task 3.2: 전체 팔로잉 목록 진입 연결**
|
||||
- 대상 파일:
|
||||
- 수정: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
||||
- 수정: `SodaLive/Sources/V2/Main/MainView.swift`
|
||||
@@ -472,3 +472,7 @@
|
||||
- 2026-06-30: `rg "struct HomeFollowingTabResponse|struct ChatRoomListItemResponse|enum CreatorActivityType|struct FollowingNewsResponse|struct FollowingCreatorRankingNewsResponse|struct FollowingContentNewsResponse|struct FollowingContentRankingNewsResponse|struct FollowingCommunityPostNewsResponse|enum FollowingNewsType|unknown\(" SodaLive/Sources/V2/Main/Home/Following/Models/HomeFollowingTabResponse.swift`로 Phase 2 응답 모델과 unknown enum 처리를 확인했다.
|
||||
- 2026-06-30: `rg "enum MainHomeFollowingApi|getFollowing|/api/v2/home/following|Authorization|requestPlain|TargetType|final class MainHomeFollowingRepository|MoyaProvider<MainHomeFollowingApi>|AnyPublisher<Response, MoyaError>" SodaLive/Sources/V2/Main/Home/Following/Repository`로 API/Repository 패턴을 확인했다.
|
||||
- 2026-06-30: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`를 실행해 빌드 성공을 확인했다. 기존 프로젝트의 duplicate build file/run script warning은 남아 있으나 Phase 1-2 컴파일 오류는 없었다.
|
||||
- 2026-06-30: Phase 3 구현을 진행해 `MainHomeFollowingCreatorSection`을 추가하고, `MainHomeView -> MainHomeFollowingView -> MainView.handleFollowingAllTap()` 경로로 `전체` 버튼을 `.followingList` 라우팅에 연결했다.
|
||||
- 2026-06-30: `rg "struct MainHomeFollowingCreatorSection|ScrollView\(\.horizontal|followingCreators|onTapCreator|onTapAll|allButtonTitle|Color\.soda400|padding\(\.horizontal, 16\)" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingCreatorSection.swift`로 팔로잉 크리에이터 섹션 요구사항을 확인했다.
|
||||
- 2026-06-30: `rg "onTapFollowingAll|followingList|FollowCreatorView" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift`로 `전체` 버튼 라우팅 연결을 확인했다.
|
||||
- 2026-06-30: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`를 재실행해 Phase 3 신규 Swift 파일 target 등록과 컴파일 성공을 확인했다.
|
||||
|
||||
Reference in New Issue
Block a user