refactor(modal): V2 확인 모달 사용처를 교체한다

This commit is contained in:
2026-07-08 16:04:01 +09:00
parent 8e086c4888
commit 809ed5ce23
9 changed files with 281 additions and 39 deletions

View File

@@ -6,14 +6,12 @@ import android.animation.ValueAnimator
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.MeasureSpec import android.view.View.MeasureSpec
import android.view.animation.Interpolator import android.view.animation.Interpolator
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
@@ -31,7 +29,6 @@ import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
@@ -53,9 +50,11 @@ import kr.co.vividnext.sodalive.mypage.MyPageViewModel
import kr.co.vividnext.sodalive.mypage.auth.Auth import kr.co.vividnext.sodalive.mypage.auth.Auth
import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.report.ProfileReportDialog
import kr.co.vividnext.sodalive.report.UserReportDialog import kr.co.vividnext.sodalive.report.UserReportDialog
import kr.co.vividnext.sodalive.settings.ContentSettingsActivity import kr.co.vividnext.sodalive.settings.ContentSettingsActivity
import kr.co.vividnext.sodalive.splash.SplashActivity
import kr.co.vividnext.sodalive.user.login.LoginActivity
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
import kr.co.vividnext.sodalive.v2.creator.channel.audio.CreatorChannelAudioFragment import kr.co.vividnext.sodalive.v2.creator.channel.audio.CreatorChannelAudioFragment
import kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragment import kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragment
@@ -75,8 +74,6 @@ import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelTitleBarS
import kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesFragment import kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesFragment
import kr.co.vividnext.sodalive.v2.main.MainV2Activity import kr.co.vividnext.sodalive.v2.main.MainV2Activity
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
import kr.co.vividnext.sodalive.splash.SplashActivity
import kr.co.vividnext.sodalive.user.login.LoginActivity
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@@ -383,14 +380,18 @@ class CreatorChannelActivity :
private fun showUserBlockDialog() { private fun showUserBlockDialog() {
val nickname = currentHeader?.nickname.orEmpty() val nickname = currentHeader?.nickname.orEmpty()
AlertDialog.Builder(this) V2ModalDialog(
.setTitle(getString(R.string.screen_live_room_block_title)) activity = this,
.setMessage(getString(R.string.screen_live_room_block_message_user, nickname)) layoutInflater = layoutInflater,
.setPositiveButton(getString(R.string.screen_live_room_block_confirm)) { _, _ -> title = getString(R.string.screen_live_room_block_title),
desc = getString(R.string.screen_live_room_block_message_user, nickname),
confirmButtonTitle = getString(R.string.screen_live_room_block_confirm),
confirmButtonClick = {
homeActionDelegate?.blockUser() homeActionDelegate?.blockUser()
} },
.setNegativeButton(getString(R.string.cancel)) { _, _ -> } cancelButtonTitle = getString(R.string.cancel),
.show() cancelButtonClick = {}
).show(screenWidth)
} }
private fun showUserReportDialog() { private fun showUserReportDialog() {
@@ -400,9 +401,18 @@ class CreatorChannelActivity :
} }
private fun showProfileReportDialog() { private fun showProfileReportDialog() {
ProfileReportDialog(this, layoutInflater) { V2ModalDialog(
activity = this,
layoutInflater = layoutInflater,
title = getString(R.string.dialog_profile_report_title),
desc = getString(R.string.dialog_profile_report_notice),
confirmButtonTitle = getString(R.string.report_button),
confirmButtonClick = {
homeActionDelegate?.reportProfile() homeActionDelegate?.reportProfile()
}.show(resources.displayMetrics.widthPixels) },
cancelButtonTitle = getString(R.string.cancel),
cancelButtonClick = {}
).show(resources.displayMetrics.widthPixels)
} }
private fun setupTabsAndPager() { private fun setupTabsAndPager() {
@@ -604,7 +614,7 @@ class CreatorChannelActivity :
} }
override fun onCreatorChannelFanTalkDeleteClicked(fanTalkId: Long) { override fun onCreatorChannelFanTalkDeleteClicked(fanTalkId: Long) {
SodaDialog( V2ModalDialog(
activity = this, activity = this,
layoutInflater = layoutInflater, layoutInflater = layoutInflater,
title = getString(R.string.screen_user_profile_cheer_delete_title), title = getString(R.string.screen_user_profile_cheer_delete_title),
@@ -640,7 +650,7 @@ class CreatorChannelActivity :
} }
private fun showCreatorChannelCommunityDeleteDialog(item: CreatorChannelCommunityPostUiModel) { private fun showCreatorChannelCommunityDeleteDialog(item: CreatorChannelCommunityPostUiModel) {
SodaDialog( V2ModalDialog(
activity = this, activity = this,
layoutInflater = layoutInflater, layoutInflater = layoutInflater,
title = getString(R.string.screen_creator_community_delete_title), title = getString(R.string.screen_creator_community_delete_title),
@@ -977,7 +987,7 @@ class CreatorChannelActivity :
if (isAdult) { if (isAdult) {
val isKoreanCountry = SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR" val isKoreanCountry = SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR"
if (isKoreanCountry && !SharedPreferenceManager.isAuth) { if (isKoreanCountry && !SharedPreferenceManager.isAuth) {
SodaDialog( V2ModalDialog(
activity = this, activity = this,
layoutInflater = layoutInflater, layoutInflater = layoutInflater,
title = getString(R.string.auth_title), title = getString(R.string.auth_title),
@@ -985,8 +995,7 @@ class CreatorChannelActivity :
confirmButtonTitle = getString(R.string.auth_go), confirmButtonTitle = getString(R.string.auth_go),
confirmButtonClick = { startAuthFlow() }, confirmButtonClick = { startAuthFlow() },
cancelButtonTitle = getString(R.string.cancel), cancelButtonTitle = getString(R.string.cancel),
cancelButtonClick = {}, cancelButtonClick = {}
descGravity = Gravity.CENTER
).show(screenWidth) ).show(screenWidth)
return return
} }

View File

@@ -3,7 +3,6 @@ package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.text.InputFilter import android.text.InputFilter
import android.view.Gravity
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.addCallback import androidx.activity.addCallback
@@ -13,8 +12,8 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelFantalkWriteBinding import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelFantalkWriteBinding
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
class CreatorChannelFanTalkWriteActivity : BaseActivity<ActivityCreatorChannelFantalkWriteBinding>( class CreatorChannelFanTalkWriteActivity : BaseActivity<ActivityCreatorChannelFantalkWriteBinding>(
@@ -121,7 +120,7 @@ class CreatorChannelFanTalkWriteActivity : BaseActivity<ActivityCreatorChannelFa
} }
private fun showCancelConfirmDialog() { private fun showCancelConfirmDialog() {
SodaDialog( V2ModalDialog(
activity = this, activity = this,
layoutInflater = layoutInflater, layoutInflater = layoutInflater,
title = getString(R.string.creator_channel_fantalk_write_exit_title), title = getString(R.string.creator_channel_fantalk_write_exit_title),
@@ -129,8 +128,7 @@ class CreatorChannelFanTalkWriteActivity : BaseActivity<ActivityCreatorChannelFa
confirmButtonTitle = getString(R.string.creator_channel_fantalk_write_exit_confirm), confirmButtonTitle = getString(R.string.creator_channel_fantalk_write_exit_confirm),
confirmButtonClick = { finish() }, confirmButtonClick = { finish() },
cancelButtonTitle = getString(R.string.creator_channel_fantalk_write_exit_cancel), cancelButtonTitle = getString(R.string.creator_channel_fantalk_write_exit_cancel),
cancelButtonClick = {}, cancelButtonClick = {}
descGravity = Gravity.CENTER
).show(screenWidth) ).show(screenWidth)
} }

View File

@@ -3,7 +3,6 @@ package kr.co.vividnext.sodalive.v2.live.onair
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Gravity
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
@@ -14,7 +13,6 @@ import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerService import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerService
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
@@ -34,6 +32,7 @@ import kr.co.vividnext.sodalive.settings.language.LanguageManager
import kr.co.vividnext.sodalive.settings.language.LocaleHelper import kr.co.vividnext.sodalive.settings.language.LocaleHelper
import kr.co.vividnext.sodalive.splash.SplashActivity import kr.co.vividnext.sodalive.splash.SplashActivity
import kr.co.vividnext.sodalive.user.login.LoginActivity import kr.co.vividnext.sodalive.user.login.LoginActivity
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
import kr.co.vividnext.sodalive.v2.live.onair.model.HomeOnAirLivePageUiState import kr.co.vividnext.sodalive.v2.live.onair.model.HomeOnAirLivePageUiState
import kr.co.vividnext.sodalive.v2.live.onair.model.canEnterHomeOnAirLiveRoom import kr.co.vividnext.sodalive.v2.live.onair.model.canEnterHomeOnAirLiveRoom
import kr.co.vividnext.sodalive.v2.live.onair.ui.HomeOnAirLiveAdapter import kr.co.vividnext.sodalive.v2.live.onair.ui.HomeOnAirLiveAdapter
@@ -183,7 +182,7 @@ class HomeOnAirLiveActivity : BaseActivity<ActivityHomeOnAirLiveBinding>(
if (isAdult) { if (isAdult) {
val isKoreanCountry = SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR" val isKoreanCountry = SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR"
if (isKoreanCountry && !SharedPreferenceManager.isAuth) { if (isKoreanCountry && !SharedPreferenceManager.isAuth) {
SodaDialog( V2ModalDialog(
activity = this, activity = this,
layoutInflater = layoutInflater, layoutInflater = layoutInflater,
title = getString(R.string.auth_title), title = getString(R.string.auth_title),
@@ -191,8 +190,7 @@ class HomeOnAirLiveActivity : BaseActivity<ActivityHomeOnAirLiveBinding>(
confirmButtonTitle = getString(R.string.auth_go), confirmButtonTitle = getString(R.string.auth_go),
confirmButtonClick = { startAuthFlow() }, confirmButtonClick = { startAuthFlow() },
cancelButtonTitle = getString(R.string.cancel), cancelButtonTitle = getString(R.string.cancel),
cancelButtonClick = {}, cancelButtonClick = {}
descGravity = Gravity.CENTER
).show(screenWidth) ).show(screenWidth)
return return
} }

View File

@@ -1,11 +1,9 @@
package kr.co.vividnext.sodalive.v2.main package kr.co.vividnext.sodalive.v2.main
import android.content.Intent import android.content.Intent
import android.view.Gravity
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.gson.Gson import com.google.gson.Gson
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.mypage.MyPageViewModel import kr.co.vividnext.sodalive.mypage.MyPageViewModel
@@ -14,6 +12,7 @@ import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.settings.ContentSettingsActivity import kr.co.vividnext.sodalive.settings.ContentSettingsActivity
import kr.co.vividnext.sodalive.splash.SplashActivity import kr.co.vividnext.sodalive.splash.SplashActivity
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
import org.koin.android.ext.android.get import org.koin.android.ext.android.get
fun Fragment.ensureMainV2NavigationAllowed( fun Fragment.ensureMainV2NavigationAllowed(
@@ -46,7 +45,7 @@ fun Fragment.ensureMainV2NavigationAllowed(
} }
private fun Fragment.showMainV2AuthDialog() { private fun Fragment.showMainV2AuthDialog() {
SodaDialog( V2ModalDialog(
activity = requireActivity(), activity = requireActivity(),
layoutInflater = layoutInflater, layoutInflater = layoutInflater,
title = getString(R.string.auth_title), title = getString(R.string.auth_title),
@@ -54,8 +53,7 @@ private fun Fragment.showMainV2AuthDialog() {
confirmButtonTitle = getString(R.string.auth_go), confirmButtonTitle = getString(R.string.auth_go),
confirmButtonClick = { startMainV2AuthFlow() }, confirmButtonClick = { startMainV2AuthFlow() },
cancelButtonTitle = getString(R.string.cancel), cancelButtonTitle = getString(R.string.cancel),
cancelButtonClick = {}, cancelButtonClick = {}
descGravity = Gravity.CENTER
).show(resources.displayMetrics.widthPixels) ).show(resources.displayMetrics.widthPixels)
} }

View File

@@ -85,6 +85,10 @@ class CreatorChannelActivitySourceTest {
assertTrue(source.contains("showUserReportDialog")) assertTrue(source.contains("showUserReportDialog"))
assertTrue(source.contains("showProfileReportDialog")) assertTrue(source.contains("showProfileReportDialog"))
assertTrue(source.contains("homeActionDelegate?.blockUser")) assertTrue(source.contains("homeActionDelegate?.blockUser"))
assertTrue(source.contains("V2ModalDialog("))
assertFalse(source.contains("AlertDialog.Builder("))
assertFalse(source.contains("import kr.co.vividnext.sodalive.report.ProfileReportDialog"))
assertFalse(source.contains("ProfileReportDialog(this, layoutInflater)"))
assertFalse(source.contains("creator_channel_more_ready")) assertFalse(source.contains("creator_channel_more_ready"))
assertTrue(sheet.contains("BottomSheetDialogFragment")) assertTrue(sheet.contains("BottomSheetDialogFragment"))
assertTrue(sheet.contains("onClickBlock")) assertTrue(sheet.contains("onClickBlock"))
@@ -599,7 +603,8 @@ class CreatorChannelActivitySourceTest {
assertTrue(source.contains("position == CreatorChannelTab.FanTalk.ordinal")) assertTrue(source.contains("position == CreatorChannelTab.FanTalk.ordinal"))
assertTrue(source.contains("override fun onCreatorChannelFanTalkContentChanged()")) assertTrue(source.contains("override fun onCreatorChannelFanTalkContentChanged()"))
assertTrue(source.contains("override fun onCreatorChannelFanTalkDeleteClicked(fanTalkId: Long)")) assertTrue(source.contains("override fun onCreatorChannelFanTalkDeleteClicked(fanTalkId: Long)"))
assertTrue(source.contains("SodaDialog(")) assertTrue(source.contains("V2ModalDialog("))
assertFalse(source.contains("SodaDialog("))
assertTrue(source.contains("R.string.screen_user_profile_cheer_delete_title")) assertTrue(source.contains("R.string.screen_user_profile_cheer_delete_title"))
assertTrue(source.contains("R.string.screen_user_profile_cheer_delete_desc")) assertTrue(source.contains("R.string.screen_user_profile_cheer_delete_desc"))
assertTrue(source.contains("R.string.confirm_delete_title")) assertTrue(source.contains("R.string.confirm_delete_title"))
@@ -1371,7 +1376,8 @@ class CreatorChannelActivitySourceTest {
assertTrue(activity.contains("liveCoordinator.enterLiveRoom(live.liveId)")) assertTrue(activity.contains("liveCoordinator.enterLiveRoom(live.liveId)"))
assertTrue(activity.contains("private fun ensureLoginAndAdultAuth(isAdult: Boolean, onAuthed: () -> Unit)")) assertTrue(activity.contains("private fun ensureLoginAndAdultAuth(isAdult: Boolean, onAuthed: () -> Unit)"))
assertTrue(activity.contains("SharedPreferenceManager.token.isBlank()")) assertTrue(activity.contains("SharedPreferenceManager.token.isBlank()"))
assertTrue(activity.contains("SodaDialog(")) assertTrue(activity.contains("V2ModalDialog("))
assertFalse(activity.contains("SodaDialog("))
assertTrue(activity.contains("R.string.auth_desc_live")) assertTrue(activity.contains("R.string.auth_desc_live"))
assertTrue(activity.contains("ContentSettingsActivity::class.java")) assertTrue(activity.contains("ContentSettingsActivity::class.java"))
assertTrue(activity.contains("Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE")) assertTrue(activity.contains("Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE"))

View File

@@ -23,6 +23,8 @@ class CreatorChannelFanTalkWriteSourceTest {
assertTrue(source.contains("WindowInsetsCompat.Type.ime()")) assertTrue(source.contains("WindowInsetsCompat.Type.ime()"))
assertTrue(source.contains("onBackPressedDispatcher.addCallback")) assertTrue(source.contains("onBackPressedDispatcher.addCallback"))
assertTrue(source.contains("showCancelConfirmDialog()")) assertTrue(source.contains("showCancelConfirmDialog()"))
assertTrue(source.contains("V2ModalDialog("))
assertFalse(source.contains("SodaDialog("))
assertTrue(source.contains("setResult(RESULT_OK)")) assertTrue(source.contains("setResult(RESULT_OK)"))
assertTrue(source.contains("viewModel.submit(creatorId)")) assertTrue(source.contains("viewModel.submit(creatorId)"))
assertTrue(manifest.contains(".v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteActivity")) assertTrue(manifest.contains(".v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteActivity"))

View File

@@ -17,7 +17,8 @@ class MainV2LoginGuardSourceTest {
assertTrue(source.contains("(activity as? MainV2Activity)?.showLoginActivity()")) assertTrue(source.contains("(activity as? MainV2Activity)?.showLoginActivity()"))
assertTrue(source.contains("SharedPreferenceManager.countryCode.ifBlank { \"KR\" }")) assertTrue(source.contains("SharedPreferenceManager.countryCode.ifBlank { \"KR\" }"))
assertTrue(source.contains("SharedPreferenceManager.isAuth")) assertTrue(source.contains("SharedPreferenceManager.isAuth"))
assertTrue(source.contains("SodaDialog(")) assertTrue(source.contains("V2ModalDialog("))
assertFalse(source.contains("SodaDialog("))
assertTrue(source.contains("Auth.auth(requireActivity(), requireContext())")) assertTrue(source.contains("Auth.auth(requireActivity(), requireContext())"))
assertTrue(source.contains("Gson().fromJson(json, BootpayResponse::class.java)")) assertTrue(source.contains("Gson().fromJson(json, BootpayResponse::class.java)"))
assertTrue(source.contains("AuthVerifyRequest(receiptId = bootpayResponse.data.receiptId)")) assertTrue(source.contains("AuthVerifyRequest(receiptId = bootpayResponse.data.receiptId)"))

View File

@@ -135,6 +135,142 @@
--- ---
### Phase 4: V2 사용처 `dialog_v2_modal` 적용 대상 조사
- [x] **Task 4.1: `kr.co.vividnext.sodalive.v2` 하위 dialog/modal 사용처 검색**
- 확인:
- `app/src/main/java/kr/co/vividnext/sodalive/v2`
- `app/src/test/java/kr/co/vividnext/sodalive/v2`
- 검색 키워드:
- `SodaDialog`
- `AlertDialog.Builder`
- `V2ModalDialog`
- `DialogFragment`
- `BottomSheetDialogFragment`
- `PopupWindow`
- `LivePaymentDialog`
- `LiveRoomPasswordDialog`
- `LiveCancelDialog`
- `LiveRoomDonationDialog`
- `UserReportDialog`
- `ProfileReportDialog`
- `CheersReportDialog`
- `NotificationSettingsDialog`
- `EventPopupDialogFragment`
- 검증 명령:
- `rg -n "SodaDialog|V2ModalDialog|AlertDialog\\.Builder|AlertDialog" app/src/main/java/kr/co/vividnext/sodalive/v2 app/src/test/java/kr/co/vividnext/sodalive/v2`
- `rg -n "SodaDialog\\(|AlertDialog\\.Builder\\(|UserReportDialog\\(|ProfileReportDialog\\(|CheersReportDialog\\(|LivePaymentDialog\\(|LiveRoomPasswordDialog\\(|LiveCancelDialog\\(|LiveRoomDonationDialog\\(|NotificationSettingsDialog\\(|EventPopupDialogFragment\\(|BottomSheetDialogFragment|PopupWindow" app/src/main/java/kr/co/vividnext/sodalive/v2`
- 기대 결과:
-`V2ModalDialog`로 직접 바꿀 수 있는 확인형 모달과 별도 설계가 필요한 커스텀 다이얼로그를 구분한다.
- 검증 기록:
- 2026-07-08 실행 결과, 직접 교체 후보는 `SodaDialog` 6곳, `AlertDialog.Builder` 1곳, `ProfileReportDialog` 1곳으로 분류했다.
- 입력/라디오/이미지/토글/BottomSheet/PopupWindow 기반 사용처는 현 `V2ModalDialog` API로 직접 표현하기 어려워 별도 설계 후보로 분류했다.
- [x] **Task 4.2: 교체 후보와 테스트 영향 문서화**
- 수정:
- `docs/20260708_V2_공통_모달창/prd.md`
- `docs/20260708_V2_공통_모달창/plan-task.md`
- 바로 교체 가능한 후보:
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuard.kt:49`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt:186`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:386`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:403`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:607`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:643`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:980`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteActivity.kt:124`
- 테스트 영향:
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuardSourceTest.kt:20`
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt:602`
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt:1374`
- 검증 명령:
- `rg -n "SodaDialog\\(|AlertDialog\\.Builder\\(|ProfileReportDialog\\(" app/src/main/java/kr/co/vividnext/sodalive/v2 app/src/test/java/kr/co/vividnext/sodalive/v2`
- 기대 결과:
- 후속 구현자가 교체 파일, 테스트 파일, 제외/보류 대상을 문서만으로 파악할 수 있다.
- 검증 기록:
- 2026-07-08 문서 작성 단계에서는 구현 변경이 없으므로 Gradle 검증을 실행하지 않았다.
---
### Phase 5: 확인형 모달 `V2ModalDialog` 교체 구현
- [x] **Task 5.1: `SodaDialog`, `AlertDialog.Builder`, `ProfileReportDialog` 직접 교체 후보를 `V2ModalDialog`로 변경**
- 수정:
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuard.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteActivity.kt`
- 작업:
- `kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog`를 import한다.
- 확인/취소형 모달의 title, desc, confirm/cancel title, confirm/cancel action을 기존 동작과 동일하게 매핑한다.
- `descGravity = Gravity.CENTER`는 현 `dialog_v2_modal` desc가 중앙 정렬이므로 제거한다.
- 교체 후 미사용 `SodaDialog`, `AlertDialog`, `Gravity`, `ProfileReportDialog` import를 제거한다.
- 레거시 다이얼로그 클래스와 레거시 layout은 수정하지 않는다.
- 검증 명령:
- `./gradlew :app:compileDebugKotlin`
- `git diff --check`
- 기대 결과:
- 직접 교체 후보가 `dialog_v2_modal` 기반으로 표시되고 컴파일 오류가 없다.
- 검증 기록:
- 2026-07-08 `MainV2LoginGuard.kt`, `HomeOnAirLiveActivity.kt`, `CreatorChannelActivity.kt`, `CreatorChannelFanTalkWriteActivity.kt`의 직접 교체 후보를 `V2ModalDialog`로 변경했다. `descGravity = Gravity.CENTER``dialog_v2_modal`의 desc 중앙 정렬 계약으로 대체되어 제거했고, 기존 title/desc/button string과 confirm/cancel callback은 유지했다.
- 2026-07-08 `rg -n "SodaDialog\(|AlertDialog\.Builder\(|ProfileReportDialog\(|descGravity|import android.view.Gravity|import kr.co.vividnext.sodalive.base.SodaDialog|import androidx.appcompat.app.AlertDialog|import kr.co.vividnext.sodalive.report.ProfileReportDialog" ...` 확인 결과, 생산 코드의 직접 교체 대상 레거시 생성자/import는 남지 않았다. 검색에 남은 `showProfileReportDialog`는 V2 모달을 여는 메서드명이다.
- [x] **Task 5.2: source 테스트 기대값 갱신**
- 수정:
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuardSourceTest.kt`
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
- 작업:
- `SodaDialog(` 문자열 기대를 `V2ModalDialog(` 또는 더 구체적인 V2 모달 import/호출 계약으로 바꾼다.
- `CreatorChannelActivity`의 사용자 차단/프로필 신고/삭제/성인 인증 확인 흐름이 기존 callback을 유지하는지 source 계약으로 확인한다.
- 검증 명령:
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2LoginGuardSourceTest"`
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"`
- 기대 결과:
- 갱신된 source 테스트가 PASS한다.
- 검증 기록:
- 2026-07-08 `MainV2LoginGuardSourceTest`, `CreatorChannelActivitySourceTest`, `CreatorChannelFanTalkWriteSourceTest`의 source 계약을 `V2ModalDialog(` 기대와 레거시 `SodaDialog(`/`AlertDialog.Builder(`/`ProfileReportDialog(this, layoutInflater)` 금지 조건으로 갱신했다.
- 2026-07-08 테스트 변경 직후 병렬 Gradle 실행은 Kotlin incremental cache 경합으로 test assertion 전에 실패했다. 이후 `--no-parallel --no-daemon -Pkotlin.incremental=false`로 직렬 실행해 `MainV2LoginGuardSourceTest`, `CreatorChannelActivitySourceTest`, `CreatorChannelFanTalkWriteSourceTest`가 PASS함을 확인했다.
- [x] **Task 5.3: 직접 교체 범위 회귀 검증**
- 실행:
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.*"`
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2LoginGuardSourceTest"`
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"`
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteSourceTest"`
- `./gradlew :app:compileDebugKotlin`
- `git diff --check`
- 기대 결과:
- 직접 교체 범위 테스트와 컴파일이 PASS한다.
- 검증 기록:
- 2026-07-08 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.*" --no-parallel --no-daemon -Pkotlin.incremental=false` PASS.
- 2026-07-08 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2LoginGuardSourceTest" --no-parallel --no-daemon -Pkotlin.incremental=false` PASS.
- 2026-07-08 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --no-parallel --no-daemon -Pkotlin.incremental=false` PASS.
- 2026-07-08 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteSourceTest" --no-parallel --no-daemon -Pkotlin.incremental=false` PASS.
- 2026-07-08 `./gradlew :app:compileDebugKotlin --no-parallel --no-daemon -Pkotlin.incremental=false` PASS.
- 2026-07-08 `git diff --check` PASS.
---
### Phase 6: 커스텀 콘텐츠 다이얼로그 후속 설계
- [ ] **Task 6.1: 현 `V2ModalDialog`로 직접 표현할 수 없는 사용처 처리 방침 결정**
- 확인 대상:
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelLiveCoordinator.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelMoreBottomSheet.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelSortPopup.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/ui/CreatorChannelFanTalkMorePopup.kt`
- 결정 필요:
- report 라디오 목록, password/reason 입력, payment 시간 안내, donation BottomSheet, notification toggle, event image popup을 `dialog_v2_modal` 기반으로 새로 설계할지 여부
- BottomSheet/PopupWindow 메뉴를 모달 교체 범위에서 제외할지 여부
- 검증 기준:
- 결정 결과를 이 문서의 Phase 6 아래에 추가하고, 구현 전 PRD를 먼저 갱신한다.
---
## Verification Log ## Verification Log
- 2026-07-08 문서 작성 단계: 구현 전이므로 Gradle 검증을 실행하지 않았다. Figma `567:17662`, `567:17666` 디자인 컨텍스트와 스크린샷으로 layout 구조/색상 값을 확정했고, `v2/components/modal` 빈 패키지와 `gray_900`/`soda_400` 색상 리소스 존재를 확인했다. - 2026-07-08 문서 작성 단계: 구현 전이므로 Gradle 검증을 실행하지 않았다. Figma `567:17662`, `567:17666` 디자인 컨텍스트와 스크린샷으로 layout 구조/색상 값을 확정했고, `v2/components/modal` 빈 패키지와 `gray_900`/`soda_400` 색상 리소스 존재를 확인했다.
@@ -144,3 +280,4 @@
- 2026-07-08 최종 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check` PASS. `./gradlew :app:ktlintCheck`는 기존 레거시/타 영역 파일의 package-name, trailing comma, indentation 등 사전 존재 위반으로 FAIL했으며, ktlint 보고서에 이번 변경 파일(`V2ModalDialog.kt`, `V2ModalDialogTest.kt`, `dialog_v2_modal.xml`, `bg_v2_modal.xml`)은 포함되지 않았다. Figma `567:17662`, `567:17666` 스크린샷 기준으로 컨테이너/타이틀/본문/버튼 영역, 버튼 1:1 구성, `gray_900`/white/`soda_400` 색상을 대조했다. - 2026-07-08 최종 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check` PASS. `./gradlew :app:ktlintCheck`는 기존 레거시/타 영역 파일의 package-name, trailing comma, indentation 등 사전 존재 위반으로 FAIL했으며, ktlint 보고서에 이번 변경 파일(`V2ModalDialog.kt`, `V2ModalDialogTest.kt`, `dialog_v2_modal.xml`, `bg_v2_modal.xml`)은 포함되지 않았다. Figma `567:17662`, `567:17666` 스크린샷 기준으로 컨테이너/타이틀/본문/버튼 영역, 버튼 1:1 구성, `gray_900`/white/`soda_400` 색상을 대조했다.
- 2026-07-08 리뷰 반영: 투명 `bg_v2_modal_button.xml`은 실제 시각 효과가 없어 제거했다. 버튼 중복 클릭 시 action이 여러 번 실행될 수 있는 위험을 막기 위해 최초 클릭 1회만 dismiss/action을 수행하도록 보완했고, 긴 title 줄바꿈 시 잘림을 줄이기 위해 title 높이를 `wrap_content` + `minHeight=64dp`로 조정했다. - 2026-07-08 리뷰 반영: 투명 `bg_v2_modal_button.xml`은 실제 시각 효과가 없어 제거했다. 버튼 중복 클릭 시 action이 여러 번 실행될 수 있는 위험을 막기 위해 최초 클릭 1회만 dismiss/action을 수행하도록 보완했고, 긴 title 줄바꿈 시 잘림을 줄이기 위해 title 높이를 `wrap_content` + `minHeight=64dp`로 조정했다.
- 2026-07-08 코드 리뷰/검증: `V2ModalDialog.kt`, `dialog_v2_modal.xml`, `bg_v2_modal.xml`, `V2ModalDialogTest.kt`를 PRD와 Figma MCP `567:17662`, `567:17666` 기준으로 재대조했으며 차단 이슈는 발견하지 않았다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check` PASS. 신규 untracked 파일은 `git diff --no-index --check /dev/null <file>`로 별도 확인했고 whitespace 오류 출력이 없었다. `./gradlew :app:ktlintCheck`는 기존 레거시/타 영역 위반으로 FAIL했으며 ktlint 보고서에서 이번 변경 파일명 검색 결과는 없었다. - 2026-07-08 코드 리뷰/검증: `V2ModalDialog.kt`, `dialog_v2_modal.xml`, `bg_v2_modal.xml`, `V2ModalDialogTest.kt`를 PRD와 Figma MCP `567:17662`, `567:17666` 기준으로 재대조했으며 차단 이슈는 발견하지 않았다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check` PASS. 신규 untracked 파일은 `git diff --no-index --check /dev/null <file>`로 별도 확인했고 whitespace 오류 출력이 없었다. `./gradlew :app:ktlintCheck`는 기존 레거시/타 영역 위반으로 FAIL했으며 ktlint 보고서에서 이번 변경 파일명 검색 결과는 없었다.
- 2026-07-08 Phase 5 구현: `MainV2LoginGuard.kt`, `HomeOnAirLiveActivity.kt`, `CreatorChannelActivity.kt`, `CreatorChannelFanTalkWriteActivity.kt`의 직접 교체 후보를 `V2ModalDialog`로 변경하고 source test 계약을 갱신했다. 병렬 RED 실행은 Kotlin incremental cache 경합으로 assertion 전 실패했으나, 이후 비증분/직렬 실행으로 `MainV2LoginGuardSourceTest`, `CreatorChannelActivitySourceTest`, `CreatorChannelFanTalkWriteSourceTest`, `v2.components.modal.*` 테스트가 PASS했다. `./gradlew :app:compileDebugKotlin --no-parallel --no-daemon -Pkotlin.incremental=false``git diff --check`도 PASS했다.

View File

@@ -110,6 +110,99 @@ Figma 디자인 그대로 layout을 구성한다.
--- ---
## 12. Addendum: V2 사용처 `dialog_v2_modal` 적용 조사
2026-07-08 후속 요청 범위는 구현 없이 `kr.co.vividnext.sodalive.v2` 하위에서 modal/dialog 계열을 사용하는 위치를 찾고, 이후 `dialog_v2_modal` 기반 `V2ModalDialog`로 바꿀 대상을 문서화하는 것이다.
### 추가 Problem
- `V2ModalDialog``dialog_v2_modal.xml`은 구현되어 있지만, V2 화면 일부는 여전히 레거시 `SodaDialog`, 기본 `AlertDialog.Builder`, 레거시 report/payment/password dialog를 호출한다.
- V2 화면의 확인/취소형 모달이 서로 다른 layout과 스타일을 사용하면 V2 디자인 일관성이 깨진다.
- 레거시 다이얼로그 클래스 자체는 다른 레거시 화면에서도 사용되므로, 저장소 원칙상 레거시 파일을 직접 수정하지 않고 V2 호출부에서 대체해야 한다.
### 추가 Goals
- V2 패키지 안의 확인/취소형 모달은 `V2ModalDialog`를 사용해 `dialog_v2_modal` 디자인을 적용한다.
- title/desc/confirm/cancel만 필요한 사용처는 우선 교체 대상으로 삼는다.
- 입력 필드, 라디오 목록, 이미지, 토글, BottomSheet, PopupWindow 등 `V2ModalDialog`의 현재 API로 표현할 수 없는 UI는 직접 교체하지 않고 별도 설계 대상으로 분리한다.
- 후속 구현 시 기존 문자열, confirm/cancel 콜백, `show(width)` 폭 계산, dismiss 후 action 실행 흐름을 유지한다.
### 추가 Non-Goals
- 이번 조사 단계에서는 Kotlin/XML 구현을 변경하지 않는다.
- `kr.co.vividnext.sodalive.base.SodaDialog`, `kr.co.vividnext.sodalive.report.*Dialog`, `kr.co.vividnext.sodalive.live.room.*Dialog` 같은 레거시 공용 클래스를 직접 수정하지 않는다.
- `LoadingDialog`, `Toast`, 정렬/더보기용 `PopupWindow`, 메뉴성 BottomSheet는 확인형 모달 교체 범위에서 제외한다.
- 입력/라디오/이미지/토글 UI를 `V2ModalDialog`에 무리하게 넣기 위한 API 확장은 이번 문서 단계에서 결정하지 않는다.
### 바로 교체 가능한 후보
아래 항목은 title/desc/확인/취소 콜백 구조라 현 `V2ModalDialog`로 직접 교체할 수 있다.
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuard.kt:49`
- 현재: `SodaDialog`
- 용도: 성인 인증 필요 안내
- 후속 변경: `V2ModalDialog`로 교체하고 `Gravity` import 제거 확인
- `app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt:186`
- 현재: `SodaDialog`
- 용도: 성인 인증 필요 안내
- 후속 변경: `V2ModalDialog`로 교체하고 `Gravity` import 제거 확인
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:386`
- 현재: `AlertDialog.Builder`
- 용도: 사용자 차단 확인
- 후속 변경: `setTitle`/`setMessage`/positive/negative 버튼을 `V2ModalDialog`의 title/desc/confirm/cancel로 매핑
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:403`
- 현재: `ProfileReportDialog`
- 용도: 프로필 사진 신고 확인
- 후속 변경: `dialog_profile_report_title`, `dialog_profile_report_notice`, `report_button`, `cancel``V2ModalDialog`로 매핑
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:607`
- 현재: `SodaDialog`
- 용도: FanTalk 삭제 확인
- 후속 변경: 기존 삭제 confirm 콜백 유지
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:643`
- 현재: `SodaDialog`
- 용도: 커뮤니티 게시글 삭제 확인
- 후속 변경: 기존 삭제 confirm 콜백 유지
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:980`
- 현재: `SodaDialog`
- 용도: 성인 인증 필요 안내
- 후속 변경: `V2ModalDialog`로 교체하고 `Gravity` import 제거 확인
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteActivity.kt:124`
- 현재: `SodaDialog`
- 용도: 글 작성 취소/이탈 확인
- 후속 변경: `V2ModalDialog`로 교체하고 `Gravity` import 제거 확인
### 추가 설계가 필요한 후보
아래 항목은 dialog/modal 계열이지만 현 `V2ModalDialog` API가 제공하지 않는 입력, 라디오, 이미지, 토글, BottomSheet 동작을 포함한다. `dialog_v2_modal` 적용 여부는 별도 V2 전용 컴포넌트 또는 `V2ModalDialog` API 확장 결정을 먼저 해야 한다.
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt:162`, `:530`
- `NotificationSettingsDialog`: 알림 토글 3개가 필요하다.
- `EventPopupDialogFragment`: 이미지형 이벤트 BottomSheet다.
- `app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt:243`, `:280`
- `LiveRoomPasswordDialog`: 비밀번호 입력과 금액 표시가 필요하다.
- `LivePaymentDialog`: 유료 라이브 결제 확인이며, 시작/현재 시각 안내 영역이 조건부로 표시된다.
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:397`, `:898`
- `UserReportDialog`: 신고 사유 `RadioGroup` 선택값이 필요하다.
- `LiveRoomDonationDialog`: 후원 금액/메시지/비밀 여부/충전 이동이 필요한 BottomSheet다.
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt:162`
- `CheersReportDialog`: 신고 사유 `RadioGroup` 선택값이 필요하다.
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelLiveCoordinator.kt:101`, `:142`, `:151`, `:195`, `:235`, `:250`
- `LiveRoomPasswordDialog`: 비밀번호 입력이 필요하다.
- `LivePaymentDialog`: 결제 확인과 조건부 시간 안내가 필요하다.
- `LiveCancelDialog`: 취소 사유 입력이 필요하다.
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelMoreBottomSheet.kt`
- `BottomSheetDialogFragment` 기반 더보기 메뉴다.
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelSortPopup.kt`
- `PopupWindow` 기반 정렬 메뉴다.
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/ui/CreatorChannelFanTalkMorePopup.kt`
- `PopupWindow` 기반 FanTalk 더보기 메뉴다.
### 테스트 영향
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuardSourceTest.kt:20`은 현재 `SodaDialog(` 문자열을 기대하므로 후속 구현 시 `V2ModalDialog(` 기대값으로 갱신해야 한다.
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt:602`, `:1374`는 현재 `SodaDialog(` 문자열을 기대하므로 후속 구현 시 `V2ModalDialog(` 또는 더 구체적인 V2 모달 계약으로 갱신해야 한다.
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteSourceTest.kt``showCancelConfirmDialog()` 흐름만 확인하므로 직접 문자열 기대 변경은 필요하지 않을 가능성이 높다.
### 추가 Open Questions
- `dialog_v2_modal` 적용 범위를 현 `V2ModalDialog`가 표현 가능한 확인/취소형 모달로 제한할지, report/password/payment/donation 같은 커스텀 콘텐츠 다이얼로그까지 V2 전용 컴포넌트로 확장할지 결정이 필요하다.
- `LivePaymentDialog`의 시작/현재 시각 안내 영역을 V2 모달 디자인 안에서 보존해야 하는지, 단순 desc로 축약해도 되는지 결정이 필요하다.
- BottomSheet와 PopupWindow 메뉴는 `dialog_v2_modal` 적용 대상에서 제외하는 것이 자연스럽지만, 디자인 정책상 V2 전용 메뉴 컴포넌트가 필요한지 별도 확인이 필요하다.
---
## Figma 참조 ## Figma 참조
- 버튼 1개: `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=567-17662&m=dev` - 버튼 1개: `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=567-17662&m=dev`
- 버튼 2개: `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=567-17666&m=dev` - 버튼 2개: `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=567-17666&m=dev`