refactor(creator): 크리에이터 접근 가드를 공통화한다
This commit is contained in:
@@ -46,14 +46,9 @@ import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.live.LiveViewModel
|
||||
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateActivity
|
||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationDialog
|
||||
import kr.co.vividnext.sodalive.mypage.MyPageViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.auth.Auth
|
||||
import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
|
||||
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
|
||||
import kr.co.vividnext.sodalive.report.UserReportDialog
|
||||
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.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.access.ensureV2Access
|
||||
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
|
||||
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.audio.CreatorChannelAudioFragment
|
||||
@@ -93,7 +88,6 @@ class CreatorChannelActivity :
|
||||
CreatorChannelDonationFragment.Host {
|
||||
|
||||
private val liveViewModel: LiveViewModel by inject()
|
||||
private val myPageViewModel: MyPageViewModel by inject()
|
||||
private val creatorCommunityRepository: CreatorCommunityRepository by inject()
|
||||
private var creatorId: Long = 0L
|
||||
private var currentHeader: CreatorChannelHeaderUiModel? = null
|
||||
@@ -180,8 +174,7 @@ class CreatorChannelActivity :
|
||||
return
|
||||
}
|
||||
|
||||
if (SharedPreferenceManager.token.isBlank()) {
|
||||
showLoginActivity()
|
||||
if (!ensureV2Access(AccessRequirement.Login)) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
@@ -227,7 +220,7 @@ class CreatorChannelActivity :
|
||||
binding.btnCreatorChannelFantalkFixedPlus.setOnClickListener { openFanTalkWrite() }
|
||||
binding.btnCreatorChannelOwnerCta.setOnClickListener { onOwnerCtaClicked() }
|
||||
binding.tvChatButton.setOnClickListener {
|
||||
ensureLoginAndAdultAuth(isAdult = true) {
|
||||
ensureV2Access(AccessRequirement.AdultContent) {
|
||||
currentHeader?.characterId?.let { characterId -> homeActionDelegate?.createChatRoom(characterId) }
|
||||
}
|
||||
}
|
||||
@@ -602,13 +595,10 @@ class CreatorChannelActivity :
|
||||
|
||||
override fun onCreatorChannelCommunityPostClicked(postId: Long) {
|
||||
if (postId <= 0L) return
|
||||
if (SharedPreferenceManager.token.isBlank()) {
|
||||
showLoginActivity()
|
||||
return
|
||||
}
|
||||
|
||||
ensureV2Access(AccessRequirement.Login) {
|
||||
communityDetailLauncher.launch(CreatorChannelCommunityDetailActivity.newIntent(this, postId))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatorChannelFanTalkContentChanged() {
|
||||
updateViewPagerHeight {
|
||||
@@ -623,15 +613,12 @@ class CreatorChannelActivity :
|
||||
override fun onCreatorChannelFanTalkItemClicked(item: CreatorChannelFanTalkUiModel) {
|
||||
if (currentHeader?.isOwner != true) return
|
||||
if (creatorId <= 0L || item.fanTalkId <= 0L) return
|
||||
if (SharedPreferenceManager.token.isBlank()) {
|
||||
showLoginActivity()
|
||||
return
|
||||
}
|
||||
|
||||
ensureV2Access(AccessRequirement.Login) {
|
||||
fanTalkDetailLauncher.launch(
|
||||
CreatorChannelFanTalkDetailActivity.newIntent(this, creatorId, item.toFanTalkDetailPayload())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatorChannelFanTalkFloatingButtonVisibilityChanged(isVisible: Boolean) {
|
||||
isFanTalkFixedPlusVisible = isVisible
|
||||
@@ -917,12 +904,10 @@ class CreatorChannelActivity :
|
||||
|
||||
private fun openFanTalkWrite() {
|
||||
if (creatorId <= 0L) return
|
||||
if (SharedPreferenceManager.token.isBlank()) {
|
||||
showLoginActivity()
|
||||
return
|
||||
}
|
||||
ensureV2Access(AccessRequirement.Login) {
|
||||
fanTalkWriteLauncher.launch(CreatorChannelFanTalkWriteActivity.newIntent(this, creatorId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun onOwnerFabCommunityClicked() {
|
||||
collapseOwnerFab(animate = false)
|
||||
@@ -974,7 +959,7 @@ class CreatorChannelActivity :
|
||||
}
|
||||
|
||||
override fun onCreatorChannelCurrentLiveClicked(live: CreatorChannelLiveResponse) {
|
||||
ensureLoginAndAdultAuth(isAdult = live.isAdult) {
|
||||
ensureV2Access(AccessRequirement.forAdultContent(live.isAdult)) {
|
||||
liveCoordinator.enterLiveRoom(live.liveId)
|
||||
}
|
||||
}
|
||||
@@ -1033,70 +1018,6 @@ class CreatorChannelActivity :
|
||||
position == CreatorChannelTab.Donation.ordinal
|
||||
}
|
||||
|
||||
private fun ensureLoginAndAdultAuth(isAdult: Boolean, onAuthed: () -> Unit) {
|
||||
if (SharedPreferenceManager.token.isBlank()) {
|
||||
showLoginActivity()
|
||||
return
|
||||
}
|
||||
|
||||
if (isAdult) {
|
||||
val isKoreanCountry = SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR"
|
||||
if (isKoreanCountry && !SharedPreferenceManager.isAuth) {
|
||||
V2ModalDialog(
|
||||
activity = this,
|
||||
layoutInflater = layoutInflater,
|
||||
title = getString(R.string.auth_title),
|
||||
desc = getString(R.string.auth_desc_live),
|
||||
confirmButtonTitle = getString(R.string.auth_go),
|
||||
confirmButtonClick = { startAuthFlow() },
|
||||
cancelButtonTitle = getString(R.string.cancel),
|
||||
cancelButtonClick = {}
|
||||
).show(screenWidth)
|
||||
return
|
||||
}
|
||||
|
||||
if (!SharedPreferenceManager.isAdultContentVisible) {
|
||||
startActivity(
|
||||
Intent(this, ContentSettingsActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE, true)
|
||||
}
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
onAuthed()
|
||||
}
|
||||
|
||||
private fun showLoginActivity() {
|
||||
if (SharedPreferenceManager.token.isBlank()) {
|
||||
startActivity(
|
||||
Intent(this, LoginActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_DATA, intent.extras)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startAuthFlow() {
|
||||
Auth.auth(this, this) { json ->
|
||||
val bootpayResponse = Gson().fromJson(json, BootpayResponse::class.java)
|
||||
val request = AuthVerifyRequest(receiptId = bootpayResponse.data.receiptId)
|
||||
runOnUiThread {
|
||||
myPageViewModel.authVerify(request) {
|
||||
startActivity(
|
||||
Intent(this, SplashActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateViewPagerHeight(afterHeightUpdated: (() -> Unit)? = null) {
|
||||
updateCreatorChannelTabViewportHeight()
|
||||
binding.viewPager.post {
|
||||
|
||||
@@ -47,31 +47,82 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(source.contains("marginStart = if (isChatVisible && isDmVisible)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Activity 진입은 creator id 확인 후 로그인 가드를 거부하면 화면 조립 전에 종료한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val setupSource = sourceSection(
|
||||
source = source,
|
||||
startMarker = "override fun setupView()",
|
||||
endMarker = "private fun setupLiveEntryObservers"
|
||||
)
|
||||
val invalidId = "if (creatorId <= 0L)"
|
||||
val loginGuard = "if (!ensureV2Access(AccessRequirement.Login))"
|
||||
val setupTabs = "setupTabsAndPager()"
|
||||
|
||||
assertTrue(setupSource.contains(invalidId))
|
||||
assertTrue(setupSource.contains(loginGuard))
|
||||
assertTrue(setupSource.indexOf(invalidId) < setupSource.indexOf(loginGuard))
|
||||
assertTrue(setupSource.indexOf(loginGuard) < setupSource.indexOf(setupTabs))
|
||||
|
||||
val loginDeniedSource = setupSource.substring(
|
||||
setupSource.indexOf(loginGuard),
|
||||
setupSource.indexOf(setupTabs)
|
||||
)
|
||||
assertTrue(loginDeniedSource.contains("finish()"))
|
||||
assertTrue(loginDeniedSource.contains("return"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AI 채팅 클릭은 성인 가드 통과 후 채팅방 생성을 요청한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val clickSource = sourceSection(
|
||||
source = source,
|
||||
startMarker = "binding.tvChatButton.setOnClickListener",
|
||||
endMarker = "binding.tvDmButton.setOnClickListener"
|
||||
)
|
||||
val adultGuard = "ensureV2Access(AccessRequirement.AdultContent)"
|
||||
val createChatRoom = "homeActionDelegate?.createChatRoom(characterId)"
|
||||
|
||||
assertTrue(clickSource.contains(adultGuard))
|
||||
assertTrue(clickSource.contains(createChatRoom))
|
||||
assertTrue(clickSource.indexOf(adultGuard) < clickSource.indexOf(createChatRoom))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `커뮤니티 게시물 클릭 source는 상세 Activity newIntent로 진입하고 invalid postId를 무시한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val handlerStart = source.indexOf("override fun onCreatorChannelCommunityPostClicked(postId: Long)")
|
||||
val handlerEnd = source.indexOf("override fun onCreatorChannelFanTalkContentChanged", handlerStart)
|
||||
|
||||
assertTrue(handlerStart >= 0)
|
||||
assertTrue(handlerEnd > handlerStart)
|
||||
val handlerSource = source.substring(handlerStart, handlerEnd)
|
||||
|
||||
assertTrue(
|
||||
source.contains(
|
||||
"import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityDetailActivity"
|
||||
)
|
||||
)
|
||||
assertTrue(source.contains("override fun onCreatorChannelCommunityPostClicked(postId: Long)"))
|
||||
assertTrue(source.contains("if (postId <= 0L) return"))
|
||||
assertTrue(source.contains("if (SharedPreferenceManager.token.isBlank())"))
|
||||
assertTrue(source.contains("showLoginActivity()"))
|
||||
assertTrue(source.contains("CreatorChannelCommunityDetailActivity.newIntent(this, postId)"))
|
||||
assertTrue(handlerSource.contains("if (postId <= 0L) return"))
|
||||
assertTrue(handlerSource.contains("ensureV2Access(AccessRequirement.Login)"))
|
||||
assertTrue(handlerSource.contains("CreatorChannelCommunityDetailActivity.newIntent(this, postId)"))
|
||||
assertTrue(source.contains("private val communityDetailLauncher = registerForActivityResult"))
|
||||
assertTrue(
|
||||
source.contains(
|
||||
handlerSource.contains(
|
||||
"communityDetailLauncher.launch(CreatorChannelCommunityDetailActivity.newIntent(this, postId))"
|
||||
)
|
||||
)
|
||||
assertTrue(
|
||||
source.indexOf("if (SharedPreferenceManager.token.isBlank())") <
|
||||
source.indexOf("communityDetailLauncher.launch(CreatorChannelCommunityDetailActivity.newIntent(this, postId))")
|
||||
handlerSource.indexOf("ensureV2Access(AccessRequirement.Login)") <
|
||||
handlerSource.indexOf(
|
||||
"communityDetailLauncher.launch(CreatorChannelCommunityDetailActivity.newIntent(this, postId))"
|
||||
)
|
||||
)
|
||||
assertTrue(source.contains("refreshCreatorChannelCommunity()"))
|
||||
}
|
||||
@@ -207,6 +258,11 @@ class CreatorChannelActivitySourceTest {
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt"
|
||||
).readText()
|
||||
val appDi = projectFile("app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt").readText()
|
||||
val writeSource = sourceSection(
|
||||
source = source,
|
||||
startMarker = "private fun openFanTalkWrite()",
|
||||
endMarker = "private fun onOwnerFabCommunityClicked"
|
||||
)
|
||||
|
||||
assertTrue(layout.contains("android:id=\"@+id/btn_creator_channel_fantalk_fixed_plus\""))
|
||||
assertTrue(layout.contains("android:src=\"@drawable/ic_plus_no_bg\""))
|
||||
@@ -214,11 +270,13 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(layout.contains("app:layout_constraintBottom_toBottomOf=\"parent\""))
|
||||
assertTrue(layout.contains("app:layout_constraintEnd_toEndOf=\"parent\""))
|
||||
assertTrue(source.contains("private val fanTalkWriteLauncher = registerForActivityResult"))
|
||||
assertTrue(source.contains("CreatorChannelFanTalkWriteActivity.newIntent(this, creatorId)"))
|
||||
assertTrue(source.contains("private fun openFanTalkWrite()"))
|
||||
assertTrue(source.contains("if (creatorId <= 0L) return"))
|
||||
assertTrue(source.contains("if (SharedPreferenceManager.token.isBlank())"))
|
||||
assertTrue(source.contains("showLoginActivity()"))
|
||||
assertTrue(writeSource.contains("if (creatorId <= 0L) return"))
|
||||
assertTrue(writeSource.contains("ensureV2Access(AccessRequirement.Login)"))
|
||||
assertTrue(writeSource.contains("CreatorChannelFanTalkWriteActivity.newIntent(this, creatorId)"))
|
||||
assertTrue(
|
||||
writeSource.indexOf("ensureV2Access(AccessRequirement.Login)") <
|
||||
writeSource.indexOf("fanTalkWriteLauncher.launch")
|
||||
)
|
||||
assertTrue(source.contains("refreshCreatorChannelFanTalk()"))
|
||||
assertTrue(source.contains("homeActionDelegate?.refreshHome()"))
|
||||
assertTrue(source.contains("onCreatorChannelFanTalkWriteClicked()"))
|
||||
@@ -1474,15 +1532,18 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(fragment.contains("private fun onCurrentLiveClicked(live: CreatorChannelLiveResponse)"))
|
||||
assertTrue(fragment.contains("host.onCreatorChannelCurrentLiveClicked(live)"))
|
||||
assertTrue(activity.contains("override fun onCreatorChannelCurrentLiveClicked(live: CreatorChannelLiveResponse)"))
|
||||
assertTrue(activity.contains("ensureLoginAndAdultAuth(isAdult = live.isAdult)"))
|
||||
assertTrue(activity.contains("ensureV2Access(AccessRequirement.forAdultContent(live.isAdult))"))
|
||||
assertTrue(activity.contains("liveCoordinator.enterLiveRoom(live.liveId)"))
|
||||
assertTrue(activity.contains("private fun ensureLoginAndAdultAuth(isAdult: Boolean, onAuthed: () -> Unit)"))
|
||||
assertTrue(activity.contains("SharedPreferenceManager.token.isBlank()"))
|
||||
assertTrue(activity.contains("V2ModalDialog("))
|
||||
assertFalse(activity.contains("SodaDialog("))
|
||||
assertTrue(activity.contains("R.string.auth_desc_live"))
|
||||
assertTrue(activity.contains("ContentSettingsActivity::class.java"))
|
||||
assertTrue(activity.contains("Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE"))
|
||||
assertFalse(activity.contains("private fun ensureLoginAndAdultAuth"))
|
||||
assertTrue(activity.contains("ensureV2Access(AccessRequirement.forAdultContent(live.isAdult))"))
|
||||
val guard = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/access/V2AccessGuard.kt").readText()
|
||||
assertTrue(guard.contains("V2ModalDialog("))
|
||||
assertFalse(guard.contains("SodaDialog("))
|
||||
assertTrue(guard.contains("R.string.auth_desc_live"))
|
||||
assertTrue(guard.contains("ContentSettingsActivity::class.java"))
|
||||
assertTrue(guard.contains("Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE"))
|
||||
assertFalse(activity.contains("ContentSettingsActivity::class.java"))
|
||||
assertFalse(activity.contains("Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1891,7 +1952,6 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(fanTalkLayout.contains("android:layout_width=\"match_parent\""))
|
||||
assertTrue(fanTalkLayout.contains("android:layout_height=\"169dp\""))
|
||||
assertTrue(fanTalkLayout.contains("android:layout_marginBottom=\"@dimen/spacing_6\""))
|
||||
assertTrue(fanTalkLayout.contains("@drawable/ic_chevron_down_white"))
|
||||
assertFalse(fanTalkLayout.contains("@+id/ll_section_items"))
|
||||
assertFalse(fanTalkLayout.contains("android:clipToOutline"))
|
||||
assertTrue(fanTalkCardView.contains("clipToOutline = true"))
|
||||
@@ -2186,21 +2246,39 @@ class CreatorChannelActivitySourceTest {
|
||||
)
|
||||
)
|
||||
assertTrue(source.contains("private val fanTalkDetailLauncher = registerForActivityResult"))
|
||||
assertTrue(source.contains("override fun onCreatorChannelFanTalkItemClicked(item: CreatorChannelFanTalkUiModel)"))
|
||||
assertTrue(source.contains("if (currentHeader?.isOwner != true) return"))
|
||||
assertTrue(source.contains("if (creatorId <= 0L || item.fanTalkId <= 0L) return"))
|
||||
assertTrue(source.contains("if (SharedPreferenceManager.token.isBlank())"))
|
||||
assertTrue(source.contains("showLoginActivity()"))
|
||||
assertTrue(source.contains("fanTalkDetailLauncher.launch("))
|
||||
val handlerSource = sourceSection(
|
||||
source = source,
|
||||
startMarker = "override fun onCreatorChannelFanTalkItemClicked(item: CreatorChannelFanTalkUiModel)",
|
||||
endMarker = "override fun onCreatorChannelFanTalkFloatingButtonVisibilityChanged"
|
||||
)
|
||||
|
||||
assertTrue(handlerSource.contains("if (currentHeader?.isOwner != true) return"))
|
||||
assertTrue(handlerSource.contains("if (creatorId <= 0L || item.fanTalkId <= 0L) return"))
|
||||
assertTrue(handlerSource.contains("ensureV2Access(AccessRequirement.Login)"))
|
||||
assertTrue(handlerSource.contains("fanTalkDetailLauncher.launch("))
|
||||
assertTrue(
|
||||
source.contains(
|
||||
handlerSource.contains(
|
||||
"CreatorChannelFanTalkDetailActivity.newIntent(this, creatorId, item.toFanTalkDetailPayload())"
|
||||
)
|
||||
)
|
||||
assertTrue(
|
||||
handlerSource.indexOf("ensureV2Access(AccessRequirement.Login)") <
|
||||
handlerSource.indexOf("fanTalkDetailLauncher.launch(")
|
||||
)
|
||||
assertTrue(source.contains("refreshCreatorChannelFanTalk()"))
|
||||
assertTrue(source.contains("homeActionDelegate?.refreshHome()"))
|
||||
}
|
||||
|
||||
private fun sourceSection(source: String, startMarker: String, endMarker: String): String {
|
||||
val start = source.indexOf(startMarker)
|
||||
val end = source.indexOf(endMarker, start)
|
||||
|
||||
assertTrue("start marker not found: $startMarker", start >= 0)
|
||||
assertTrue("end marker not found after: $startMarker", end > start)
|
||||
|
||||
return source.substring(start, end)
|
||||
}
|
||||
|
||||
private fun projectFile(relativePath: String): File {
|
||||
val candidates = listOf(File(relativePath), File("../$relativePath"))
|
||||
return candidates.firstOrNull { it.exists() }
|
||||
|
||||
Reference in New Issue
Block a user