From b84098b15effe159a61b1efd07a1150f39af6f71 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Fri, 3 Jul 2026 18:57:17 +0900 Subject: [PATCH] =?UTF-8?q?feat(creator):=20=EB=8C=80=ED=99=94=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=A7=84=EC=9E=85?= =?UTF-8?q?=EC=9D=84=20=EC=97=B0=EA=B2=B0=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreatorChannel/CreatorChannelView.swift | 10 ++++-- .../CreatorChannelViewModel.swift | 36 +++++++++++++++++++ .../CreatorChannelHomeRepository.swift | 5 +++ docs/20260701_크리에이터_채널_홈/plan-task.md | 14 ++++++-- 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift b/SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift index 7db5895e..adf4fa6d 100644 --- a/SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift +++ b/SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift @@ -313,7 +313,7 @@ struct CreatorChannelView: View { if isKoreanCountry && auth == false { pendingAction = { - AppState.shared.setAppStep(step: .characterDetail(characterId: characterId)) + startChat(characterId: characterId) } isShowAuthConfirmView = true return @@ -325,7 +325,13 @@ struct CreatorChannelView: View { return } - AppState.shared.setAppStep(step: .characterDetail(characterId: characterId)) + startChat(characterId: characterId) + } + + private func startChat(characterId: Int) { + viewModel.createChatRoom(characterId: characterId) { chatRoomId in + AppState.shared.setAppStep(step: .chatRoom(id: chatRoomId)) + } } private func moveToContentSettingsWithGuideToast() { diff --git a/SodaLive/Sources/V2/CreatorChannel/CreatorChannelViewModel.swift b/SodaLive/Sources/V2/CreatorChannel/CreatorChannelViewModel.swift index 7a6496a8..41f6ceba 100644 --- a/SodaLive/Sources/V2/CreatorChannel/CreatorChannelViewModel.swift +++ b/SodaLive/Sources/V2/CreatorChannel/CreatorChannelViewModel.swift @@ -121,6 +121,42 @@ final class CreatorChannelViewModel: ObservableObject { .store(in: &subscription) } + func createChatRoom(characterId: Int, onSuccess: @escaping (Int) -> Void) { + guard characterId > 0 else { return } + isLoading = true + + repository.createChatRoom(characterId: characterId) + .receive(on: DispatchQueue.main) + .sink { [weak self] result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + self?.isLoading = false + } + } receiveValue: { [weak self] response in + self?.isLoading = false + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse.self, from: response.data) + + if let data = decoded.data, decoded.success { + onSuccess(data.chatRoomId) + } else { + self?.errorMessage = decoded.message ?? I18n.Common.commonError + self?.isShowPopup = true + } + } catch { + ERROR_LOG(error.localizedDescription) + self?.errorMessage = I18n.Common.commonError + self?.isShowPopup = true + } + } + .store(in: &subscription) + } + private func applyApiFailedPlaceholderState() { response = nil isApiFailedPlaceholderVisible = true diff --git a/SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeRepository.swift b/SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeRepository.swift index 1835dff5..949adc45 100644 --- a/SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeRepository.swift +++ b/SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeRepository.swift @@ -6,8 +6,13 @@ import Moya final class CreatorChannelHomeRepository { private let api = MoyaProvider() + private let talkApi = MoyaProvider() func getHome(creatorId: Int) -> AnyPublisher { return api.requestPublisher(.getHome(creatorId: creatorId)) } + + func createChatRoom(characterId: Int) -> AnyPublisher { + return talkApi.requestPublisher(.createChatRoom(request: CreateChatRoomRequest(characterId: characterId))) + } } diff --git a/docs/20260701_크리에이터_채널_홈/plan-task.md b/docs/20260701_크리에이터_채널_홈/plan-task.md index 0ef9f677..232c0a82 100644 --- a/docs/20260701_크리에이터_채널_홈/plan-task.md +++ b/docs/20260701_크리에이터_채널_홈/plan-task.md @@ -559,7 +559,11 @@ - [x] **Task 17.2: 기존 AI 채팅방 진입 guard 연결** - 대상 파일: - 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` + - 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelViewModel.swift` + - 수정: `SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeRepository.swift` - 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` + - 확인: `SodaLive/Sources/Chat/Character/Detail/CharacterDetailView.swift` + - 확인: `SodaLive/Sources/Chat/Character/Detail/CharacterDetailViewModel.swift` - 확인: `SodaLive/Sources/Chat/ChatTabView.swift` - 확인: `SodaLive/Sources/App/AppStep.swift` - 작업 내용: @@ -568,10 +572,13 @@ - 국가 코드를 trim/uppercase 처리하고, 비어 있거나 `KR`이면 한국으로 취급한다. - 한국 사용자는 `auth == false`일 때 기존 본인인증 confirm/fullScreenCover 흐름을 재사용하거나 같은 조건으로 연결한다. - 비한국 사용자는 기존 `ChatTabView`의 비한국 분기와 동일하게 `auth` 조건 비교를 적용한다. - - 본인인증과 콘텐츠 보기 설정 guard를 통과하면 `creator.characterId`로 기존 AI 채팅방/캐릭터 상세 진입 흐름을 연결한다. + - 본인인증과 콘텐츠 보기 설정 guard를 통과하면 `creator.characterId`로 `TalkApi.createChatRoom`을 호출하고, 성공 응답의 `chatRoomId`로 `.chatRoom(id:)`에 바로 진입한다. + - 본인인증 완료 후 실행되는 `pendingAction`도 `.characterDetail(characterId:)`가 아니라 같은 채팅방 생성 후 `.chatRoom(id:)` 진입 helper를 사용한다. - 검증 기준: - - 실행 명령: `rg "onTapTalk|countryCode|uppercased\\(\\)|isKoreanCountry|auth == false|isShowAuthConfirmView|characterId|characterDetail|setAppStep\\(step: \\.login\\)" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - - 기대 결과: 기존 AI 채팅 guard와 동일한 국가/본인인증 분기가 확인된다. + - 실행 명령: `rg "onTapTalk|countryCode|uppercased\\(\\)|isKoreanCountry|auth == false|isShowAuthConfirmView|createChatRoom|chatRoom\\(id:|setAppStep\\(step: \\.login\\)" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/CreatorChannelViewModel.swift SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeRepository.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` + - 기대 결과: 기존 AI 채팅 guard와 동일한 국가/본인인증 분기가 유지되고, guard 통과 후 캐릭터 상세가 아닌 채팅방으로 직접 이동한다. + - 실행 명령: `rg "characterDetail\\(characterId: characterId\\)" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` + - 기대 결과: 검색 결과가 없다. ### Phase 18: 최종 조립과 검증 @@ -648,3 +655,4 @@ - 2026-07-02: Task 3.2 구현을 완료하고 `CreatorChannelHeaderSection`을 공통 shell에 연결했다. `rg "struct CreatorChannelHeaderSection|CreatorChannelCreatorResponse|profileImageUrl|ignoresSafeArea\\(.*top|nickname|followerCount|DownsampledKFImage|KFImage" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelHeaderSection.swift SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`와 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 성공으로 검증했다. - 2026-07-02: Task 3.1 title bar 컴포넌트를 `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift`로 생성하고 `CreatorChannelView`의 inline title bar를 교체했다. `rg "struct CreatorChannelTitleBar|isFollow|isNotify|backgroundProgress|onTapBack|onTapFollow|onTapUnfollow|onTapNotify|onTapUnnotify|onTapMore|ic_new_bar_back|ic_new_follow|ic_new_following|ic_new_more|ic_bar_bell|ic_bar_bell_fill|팔로우" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift`와 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 성공으로 검증했다. - 2026-07-02: 사용자 확인 사항을 반영해 HeaderView는 `CreatorChannelHomeResponse.creator` 데이터로 채우고, 별도 프로필 이미지 없이 큰 배경 이미지만 표시하며, title bar는 기본 투명 배경으로 header 위에 overlay되도록 Task 3.2/3.4/3.5와 최종 검증 기준을 갱신했다. +- 2026-07-03: 사용자 요청에 따라 Task 17.2의 대화하기 액션을 캐릭터 상세 이동이 아니라 기존 Character Detail 하단 `대화하기`와 동일한 `TalkApi.createChatRoom` 호출 후 `.chatRoom(id:)` 직접 진입으로 변경했다. `CreatorChannelView`의 로그인/한국 본인인증/content settings guard는 유지했고, 본인인증 완료 후 `pendingAction`도 같은 `startChat(characterId:)` helper를 사용하도록 연결했다. `rg -n "characterDetail\\(characterId: characterId\\)|startChat\\(characterId:|createChatRoom\\(characterId:|chatRoom\\(id:" "SodaLive/Sources/V2/CreatorChannel"`와 ast-grep `AppState.shared.setAppStep(step: .characterDetail(characterId: $ID))` 검색으로 CreatorChannel 대화하기 경로에 캐릭터 상세 이동이 남지 않았음을 확인했다. 테스트는 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test` 실행 결과 `Scheme SodaLive-dev is not currently configured for the test action.`으로 RED/GREEN 자동화가 불가했다. 빌드는 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`가 `BUILD SUCCEEDED`로 완료됐다.