From 403eee262f1e5f5d085662df05fe1c8f40ab4416 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Wed, 8 Jul 2026 03:45:32 +0900 Subject: [PATCH] =?UTF-8?q?feat(main):=20=EC=BD=98=ED=85=90=EC=B8=A0=20?= =?UTF-8?q?=ED=83=AD=20=ED=83=80=EC=9D=B4=ED=8B=80=EB=B0=94=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../v2/ic_bar_storage.imageset/Contents.json | 21 ++++++++++++++++++ .../ic_bar_storage.png | Bin 0 -> 361 bytes .../V2/Main/Content/MainContentView.swift | 10 ++++----- SodaLive/Sources/V2/Main/MainView.swift | 6 ++++- .../plan-task.md | 18 ++++++++++++++- docs/20260705_메인_콘텐츠_내부_추천_탭/prd.md | 6 +++++ docs/plan-task/20260519_메인페이지신규개발.md | 3 +++ ...20260519_재사용타이틀탭텍스트바컴포넌트.md | 1 + 8 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 SodaLive/Resources/Assets.xcassets/v2/ic_bar_storage.imageset/Contents.json create mode 100644 SodaLive/Resources/Assets.xcassets/v2/ic_bar_storage.imageset/ic_bar_storage.png diff --git a/SodaLive/Resources/Assets.xcassets/v2/ic_bar_storage.imageset/Contents.json b/SodaLive/Resources/Assets.xcassets/v2/ic_bar_storage.imageset/Contents.json new file mode 100644 index 00000000..e681771d --- /dev/null +++ b/SodaLive/Resources/Assets.xcassets/v2/ic_bar_storage.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_bar_storage.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SodaLive/Resources/Assets.xcassets/v2/ic_bar_storage.imageset/ic_bar_storage.png b/SodaLive/Resources/Assets.xcassets/v2/ic_bar_storage.imageset/ic_bar_storage.png new file mode 100644 index 0000000000000000000000000000000000000000..b2e86d32f24e39952a5ba18a4f2151e99fc962d4 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O(Kz7p4nlHmNblJdl&R0g-q^xVXG8>b}$WuADt zIEGX(zMb-(x5+@@XgnwL3$~sH=@)EPjmj3y(mNV+66P?NU*NsMTf$VsyH-=|jr^hQ z|0bPY>b`#O@q>(Zf138Ygyt4m)*%K zHqLPcS6-i~_~xcvsAGI9M!)E>qwDIaUF?te^)sYpe`qBn>y>bC)m+BE^i=Fj-)#$+ zRxWZ=aITb8Q~Q4N$f>fD*YiKjT>t37sjs050-e*DLRW6SWvBMuahJlYCiPqPMpKyH z+PuE-T{M(qzUMWbOA%Z>Gfiy#bDrG$Z>(W>ggs-f=Z9BQqwInHW$<+Mb6Mw<&;$VP CEQ!ki literal 0 HcmV?d00001 diff --git a/SodaLive/Sources/V2/Main/Content/MainContentView.swift b/SodaLive/Sources/V2/Main/Content/MainContentView.swift index 636e76da..53053c88 100644 --- a/SodaLive/Sources/V2/Main/Content/MainContentView.swift +++ b/SodaLive/Sources/V2/Main/Content/MainContentView.swift @@ -3,7 +3,7 @@ import SwiftUI struct MainContentView: View { let onTapCanCharge: () -> Void let onTapSearch: () -> Void - let onTapNotificationList: () -> Void + let onTapStorage: () -> Void let onTapContent: (Int) -> Void let onTapBanner: (AudioBannerResponse) -> Void let onTapSeries: (Int) -> Void @@ -14,7 +14,7 @@ struct MainContentView: View { var body: some View { VStack(spacing: 0) { - HomeTitleBar { + DefaultTitleBar(title: MainTab.content.title) { HStack(spacing: SodaSpacing.s14) { Image("ic_bar_cash") .onTapGesture { onTapCanCharge() } @@ -22,8 +22,8 @@ struct MainContentView: View { Image("ic_bar_search") .onTapGesture { onTapSearch() } - Image("ic_bar_bell") - .onTapGesture { onTapNotificationList() } + Image("ic_bar_storage") + .onTapGesture { onTapStorage() } } } @@ -75,7 +75,7 @@ struct MainContentView_Previews: PreviewProvider { MainContentView( onTapCanCharge: {}, onTapSearch: {}, - onTapNotificationList: {}, + onTapStorage: {}, onTapContent: { _ in }, onTapBanner: { _ in }, onTapSeries: { _ in }, diff --git a/SodaLive/Sources/V2/Main/MainView.swift b/SodaLive/Sources/V2/Main/MainView.swift index 95f03433..6848ccb0 100644 --- a/SodaLive/Sources/V2/Main/MainView.swift +++ b/SodaLive/Sources/V2/Main/MainView.swift @@ -175,7 +175,7 @@ struct MainView: View { MainContentView( onTapCanCharge: handleHomeCanChargeTap, onTapSearch: handleHomeSearchTap, - onTapNotificationList: handleHomeNotificationListTap, + onTapStorage: handleContentStorageTap, onTapContent: handleRecommendationContentTap, onTapBanner: handleContentAudioBannerTap, onTapSeries: handleContentOriginalSeriesTap, @@ -583,6 +583,10 @@ struct MainView: View { } } + private func handleContentStorageTap() { + AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist)) + } + private func handleContentOriginalSeriesTap(seriesId: Int) { guard seriesId > 0 else { return } performRecommendationDetailAction { diff --git a/docs/20260705_메인_콘텐츠_내부_추천_탭/plan-task.md b/docs/20260705_메인_콘텐츠_내부_추천_탭/plan-task.md index 3a433183..81853c8b 100644 --- a/docs/20260705_메인_콘텐츠_내부_추천_탭/plan-task.md +++ b/docs/20260705_메인_콘텐츠_내부_추천_탭/plan-task.md @@ -8,6 +8,7 @@ - 문서 정책: `docs/agent-guides/documentation-policy.md` - Figma 전체 화면: `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=24-6737&m=dev` - Figma 전체 화면 node: `24:6737` +- Figma title bar: `24:6738` - Figma 배너: `24:6741` - Figma 새로 올라온 오디오: `24:6751` - Figma New&Hot: `24:6758` @@ -24,6 +25,7 @@ - 메인 하단 `콘텐츠` 탭 안에 내부 `추천` 탭 화면을 구현한다. - API endpoint는 `GET /api/v2/audio/recommendations`를 사용한다. - title bar와 내부 tab bar는 고정하고, 배너부터 하위 콘텐츠만 세로 스크롤한다. +- title bar는 Figma `24:6738` 기준으로 좌측에 메인 탭명 `콘텐츠`, 우측에 `cash`/`search`/`storage` 액션을 표시한다. - Figma에 보이는 섹션별로 Phase를 분리한다. - 이번 범위에서 `추천 시리즈`, `키워드의 오디오`는 구현하지 않는다. - `V2` 패키지 하위에 구현된 재사용 가능한 위젯 후보를 먼저 확인하고 필요한 경우 최소 보완한다. @@ -62,6 +64,7 @@ - 여러 화면에서 재사용 근거가 있는 UI만 `SodaLive/Sources/V2/Component/**`로 승격한다. - `MainView`는 `.content` 탭에서 신규 콘텐츠 탭 root를 보여주고, 상세 진입 guard callback만 전달한다. - 콘텐츠 탭 root는 title bar와 내부 tab bar를 고정 영역으로 두고, 추천 탭 body 내부에서만 `ScrollView`를 사용한다. +- 콘텐츠 탭 root의 title bar는 `DefaultTitleBar(title: MainTab.content.title)`를 사용하고, 홈 탭은 기존 `HomeTitleBar`를 유지한다. - 섹션 배열이 비어 있으면 해당 섹션을 숨긴다. - 전체 응답이 비어 있거나 API 실패/디코딩 실패 시 최소 empty 또는 error 상태를 표시한다. - 오디오 상세 진입은 기존 `handleRecommendationContentTap(contentId:)`의 로그인/성인 콘텐츠 guard 흐름을 재사용한다. @@ -93,8 +96,10 @@ ## 재사용 가능한 V2 위젯 후보 +- `SodaLive/Sources/V2/Component/DefaultTitleBar.swift` + - 콘텐츠 탭 title bar의 좌측 고정 title과 우측 액션 영역 기준 후보. - `SodaLive/Sources/V2/Component/HomeTitleBar.swift` - - 콘텐츠 탭 title bar와 우측 액션 영역의 기준 후보. + - 홈 탭 title bar 기준 후보이며, 콘텐츠 탭에는 사용하지 않는다. - `SodaLive/Sources/V2/Component/TextTabBar.swift` - 내부 `추천` 탭 bar 구성 후보. - `SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift` @@ -635,3 +640,14 @@ - 새로 올라온 오디오, 무료 오디오, 포인트 오디오, 추천 오디오 섹션이 `AudioContentThumbnailCard`를 사용하도록 정리했다. - 기존 `MainContentAudioTagOverlay.swift`를 삭제하고 신규 공용 card 파일을 Xcode project에 추가했다. - `plutil`은 `OK`, `xcodebuild`는 `BUILD SUCCEEDED`로 통과했다. +- 2026-07-08 콘텐츠 탭 title bar Figma 반영: + - 실행/확인: + - Figma node `24:6738` + - `SodaLive/Sources/V2/Main/Content/MainContentView.swift` + - `SodaLive/Sources/V2/Main/MainView.swift` + - `SodaLive/Sources/V2/Component/DefaultTitleBar.swift` + - `SodaLive/Sources/V2/Component/HomeTitleBar.swift` + - 결과: + - 콘텐츠 탭 title bar를 `DefaultTitleBar(title: MainTab.content.title)`로 변경해 좌측에 메인 탭명 `콘텐츠`를 고정 표시하도록 반영했다. + - 우측 액션은 `ic_bar_cash`, `ic_bar_search`, `ic_bar_storage` 순서로 정리하고, `ic_bar_storage`는 `MainContentView.onTapStorage`와 `MainView.handleContentStorageTap`을 통해 `.myBox(currentTab: .orderlist)`로 이동하도록 연결했다. + - 홈 탭은 기존 `HomeTitleBar`와 `ic_bar_bell` 동작을 유지한다. diff --git a/docs/20260705_메인_콘텐츠_내부_추천_탭/prd.md b/docs/20260705_메인_콘텐츠_내부_추천_탭/prd.md index 85feb48d..fc260d9f 100644 --- a/docs/20260705_메인_콘텐츠_내부_추천_탭/prd.md +++ b/docs/20260705_메인_콘텐츠_내부_추천_탭/prd.md @@ -7,6 +7,7 @@ Figma 참조: - 전체 화면: `24:6737`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=24-6737&m=dev` +- title bar: `24:6738` - 배너: `24:6741` - 새로 올라온 오디오: `24:6751` - New&Hot: `24:6758` @@ -85,6 +86,8 @@ Swift 모델은 기존 V2 관례에 맞춰 `Decodable` 구조체로 선언한다 ### 7.3 Fixed shell and scrolling - 콘텐츠 탭 title bar는 화면 상단 고정 영역이다. +- 콘텐츠 탭 title bar 좌측은 홈 로고나 내부 탭명이 아니라 메인 탭명 `콘텐츠`를 고정 표시한다. +- 콘텐츠 탭 title bar 우측은 Figma `24:6738` 기준 `cash`, `search`, `storage` 액션 아이콘을 표시하고 알림 bell 아이콘은 사용하지 않는다. - 내부 tab bar도 title bar 아래 고정 영역이다. - Figma `24:6741` 배너부터 하위 콘텐츠만 세로 `ScrollView` 안에 둔다. - 하단 main tab bar와 mini player 영역은 기존 `MainView` 구조를 유지한다. @@ -119,6 +122,7 @@ Swift 모델은 기존 V2 관례에 맞춰 `Decodable` 구조체로 선언한다 - `오직 보이스온에서만!`의 `ONLY` 표시는 `ic_series_original` + `img_new_only` asset 조합을 사용한다. ### 7.8 Navigation +- title bar의 storage 아이콘 탭은 기존 보관함 진입 경로인 `AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist))`를 사용한다. - 오디오 item 탭은 `audioContentId`로 기존 content detail 진입 흐름을 사용한다. - 배너 탭은 `eventItem`, `creatorId`, `seriesId`, `link`를 명시적으로 분기한다. - `eventItem`은 기존 event detail 흐름이 있으면 재사용한다. @@ -152,6 +156,7 @@ Swift 모델은 기존 V2 관례에 맞춰 `Decodable` 구조체로 선언한다 ## 10. Success Criteria - `.content` 탭에서 placeholder 대신 콘텐츠 탭 root가 표시된다. - 콘텐츠 탭 내부 `추천` 탭 진입 시 `GET /api/v2/audio/recommendations` 호출이 발생한다. +- 콘텐츠 탭 title bar는 좌측 고정 title `콘텐츠`와 우측 `cash`/`search`/`storage` 액션을 표시하고, storage 탭 시 보관함으로 이동한다. - title bar와 내부 tab bar는 고정되고, 배너부터 하위 섹션만 세로 스크롤된다. - API 응답의 구현 대상 배열이 각 Figma 섹션에 매핑된다. - `추천 시리즈`, `#키워드의 오디오` 섹션은 구현되지 않는다. @@ -170,3 +175,4 @@ Swift 모델은 기존 V2 관례에 맞춰 `Decodable` 구조체로 선언한다 - 2026-07-05: `MainView`, `MainHomeView`, `AudioContentCard`, `BannerCarousel`, `CreatorChannelAudioContentListItem`, `CreatorChannelSeriesListItem`을 확인해 재사용 후보를 정리했다. - 2026-07-05: 최초 문서 작성 시 `plan-task.md`만 작성되어 신규 기능 문서 정책과 맞지 않았다. 본 PRD를 추가해 정책 누락을 보완한다. - 2026-07-05: 사용자 확인으로 댓글 preview bubble에는 nickname을 표시하지 않고, 배너 `link`는 deep link 우선 후 외부 브라우저로 열며, `seriesId`는 `seriesDetail` route로 이동하도록 확정했다. +- 2026-07-08: Figma `24:6738` 기준으로 콘텐츠 탭 title bar가 홈 탭 로고/bell 구성이 아니라 좌측 고정 title `콘텐츠`와 우측 `ic_bar_cash`/`ic_bar_search`/`ic_bar_storage` 구성을 사용하도록 요구사항을 보완했다. diff --git a/docs/plan-task/20260519_메인페이지신규개발.md b/docs/plan-task/20260519_메인페이지신규개발.md index 06d9c7a9..47de1125 100644 --- a/docs/plan-task/20260519_메인페이지신규개발.md +++ b/docs/plan-task/20260519_메인페이지신규개발.md @@ -648,6 +648,9 @@ Expected: 현재 저장소 상태상 테스트 번들 타깃이 없으면 `Schem - 무엇/왜/어떻게: 구현 후 코드 리뷰를 요청해 요구사항 누락과 품질 이슈를 점검했다. - 실행 명령/도구: `task(category="deep")` 코드 리뷰 및 follow-up 검토 - 결과: 현재 범위는 `홈/콘텐츠/채팅/마이` 중 `홈/콘텐츠/채팅`이 placeholder이므로 Bootpay 인증 트리거가 없는 것은 blocking issue가 아니며, 향후 실제 라이브/성인 콘텐츠 진입 UI 연결 시 `isShowAuthConfirmView`/`pendingAction` 경로를 연결해야 한다는 non-blocking future integration note로 정리했다. +- 2026-07-08 메인 콘텐츠 탭 title bar 요구사항 반영: Figma node `24:6738` 기준으로 콘텐츠 탭 상단은 홈 탭과 같은 좌측 로고/우측 알림 버튼 구성이 아니라, 좌측에 메인 탭명 `콘텐츠`를 고정 표시하고 우측에는 `ic_bar_cash`, `ic_bar_search`, `ic_bar_storage`를 표시하도록 수정했다. +- 2026-07-08 메인 콘텐츠 탭 보관함 이동 반영: `ic_bar_storage` 터치 시 기존 보관함 진입 경로인 `AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist))`로 이동하도록 `MainContentView`에 `onTapStorage` 콜백을 추가하고 `MainView`에서 연결했다. 홈 탭의 `HomeTitleBar`/`ic_bar_bell` 동작은 유지한다. +- 2026-07-08 검증: Figma `24:6738` design context와 screenshot을 확인했고, `rg`/`ast-grep`로 콘텐츠 탭은 `DefaultTitleBar(title: MainTab.content.title)`와 `ic_bar_storage`, 홈 탭은 기존 `HomeTitleBar`와 `ic_bar_bell`을 사용하는 것을 확인했다. 구현 직후 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -destination 'generic/platform=iOS Simulator' -configuration Debug build` 실행 결과 빌드 성공을 확인했다. ## 커밋 정책 diff --git a/docs/plan-task/20260519_재사용타이틀탭텍스트바컴포넌트.md b/docs/plan-task/20260519_재사용타이틀탭텍스트바컴포넌트.md index 0ea00e19..1b5bf6ab 100644 --- a/docs/plan-task/20260519_재사용타이틀탭텍스트바컴포넌트.md +++ b/docs/plan-task/20260519_재사용타이틀탭텍스트바컴포넌트.md @@ -131,3 +131,4 @@ - 2026-05-19 추가 구현: Figma node `20:3590` 기준 캡슐형 탭 바를 `SodaLive/Sources/V2/Component/CapsuleTabBar.swift`로 추가했다. 항목 수 제한 없이 `ScrollView(.horizontal, showsIndicators: false)`와 `LazyHStack`으로 가로 스크롤되도록 구현했고, 선택 상태는 외부 `Binding`으로 관리한다. - 2026-05-19 추가 구현: `SodaLive.xcodeproj/project.pbxproj`에 `CapsuleTabBar.swift`를 `SodaLive`, `SodaLive-dev` 두 앱 타깃 Sources에 등록했다. - 2026-05-19 빌드 검증: `xcodebuild -workspace 'SodaLive.xcworkspace' -scheme 'SodaLive-dev' -configuration Debug build` 실행 결과 `** BUILD SUCCEEDED **`. +- 2026-07-08 메인 콘텐츠 탭 title bar 반영: Figma node `24:6738` 기준으로 `DefaultTitleBar(title:)`가 좌측 화면명 텍스트와 우측 메뉴 슬롯을 재사용할 수 있음을 확인했고, `SodaLive/Sources/V2/Main/Content/MainContentView.swift`에서 `HomeTitleBar` 대신 `DefaultTitleBar(title: MainTab.content.title)`를 사용하도록 반영했다. `HomeTitleBar`는 홈 탭 로고형 title bar 책임을 유지한다.