refactor(v2): 도메인 액션 경로를 통합한다
This commit is contained in:
@@ -5,19 +5,22 @@ import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
|
||||
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
|
||||
import kr.co.vividnext.sodalive.app.SodaLiveApp
|
||||
import kr.co.vividnext.sodalive.audition.AuditionActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
|
||||
import kr.co.vividnext.sodalive.message.MessageActivity
|
||||
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentActivity
|
||||
import kr.co.vividnext.sodalive.splash.SplashActivity
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
import kr.co.vividnext.sodalive.v2.chat.action.ChatActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.chat.action.handleChatAction
|
||||
import kr.co.vividnext.sodalive.v2.community.action.CommunityActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.community.action.handleCommunityAction
|
||||
import kr.co.vividnext.sodalive.v2.content.action.ContentActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.content.action.handleContentAction
|
||||
import kr.co.vividnext.sodalive.v2.creator.action.CreatorActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.creator.action.handleCreatorAction
|
||||
import kr.co.vividnext.sodalive.v2.main.MainV2Activity
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
|
||||
import java.util.Locale
|
||||
|
||||
class DeepLinkActivity : AppCompatActivity() {
|
||||
@@ -276,7 +279,7 @@ class DeepLinkActivity : AppCompatActivity() {
|
||||
?: bundle.getLong(Constants.EXTRA_COMMUNITY_POST_ID).takeIf { it > 0 }
|
||||
|
||||
if (isDmChatDeepLink(bundle) && roomId != null && roomId > 0) {
|
||||
startActivity(DmChatRoomActivity.newIntentByRoomId(applicationContext, roomId))
|
||||
handleChatAction(ChatActionCommand.DmRoom(roomId))
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -286,36 +289,29 @@ class DeepLinkActivity : AppCompatActivity() {
|
||||
return true
|
||||
}
|
||||
|
||||
communityPostId != null && communityPostId > 0 -> {
|
||||
handleCommunityAction(CommunityActionCommand.PostDetail(communityPostId))
|
||||
return true
|
||||
}
|
||||
|
||||
channelId != null && channelId > 0 -> {
|
||||
startActivity(
|
||||
CreatorChannelActivity.newIntent(applicationContext, channelId)
|
||||
)
|
||||
handleCreatorAction(CreatorActionCommand.Profile(channelId))
|
||||
return true
|
||||
}
|
||||
|
||||
contentId != null && contentId > 0 -> {
|
||||
startActivity(
|
||||
Intent(applicationContext, AudioContentDetailActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId)
|
||||
}
|
||||
)
|
||||
handleContentAction(ContentActionCommand.AudioDetail(contentId))
|
||||
return true
|
||||
}
|
||||
|
||||
messageId != null && messageId > 0 -> {
|
||||
// messageId는 DM room ID가 아니므로 과거 알림 수신 호환만 유지한다.
|
||||
startActivity(Intent(applicationContext, MessageActivity::class.java))
|
||||
return true
|
||||
}
|
||||
|
||||
communityCreatorId != null && communityCreatorId > 0 -> {
|
||||
startActivity(
|
||||
Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, communityCreatorId)
|
||||
if (communityPostId != null && communityPostId > 0) {
|
||||
putExtra(Constants.EXTRA_COMMUNITY_POST_ID, communityPostId)
|
||||
}
|
||||
}
|
||||
)
|
||||
handleCreatorAction(CreatorActionCommand.Profile(communityCreatorId))
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -345,11 +341,7 @@ class DeepLinkActivity : AppCompatActivity() {
|
||||
return false
|
||||
}
|
||||
|
||||
startActivity(
|
||||
Intent(applicationContext, SeriesDetailActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_SERIES_ID, deepLinkValueId)
|
||||
}
|
||||
)
|
||||
handleContentAction(ContentActionCommand.SeriesDetail(deepLinkValueId))
|
||||
true
|
||||
}
|
||||
|
||||
@@ -358,11 +350,7 @@ class DeepLinkActivity : AppCompatActivity() {
|
||||
return false
|
||||
}
|
||||
|
||||
startActivity(
|
||||
Intent(applicationContext, AudioContentDetailActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, deepLinkValueId)
|
||||
}
|
||||
)
|
||||
handleContentAction(ContentActionCommand.AudioDetail(deepLinkValueId))
|
||||
true
|
||||
}
|
||||
|
||||
@@ -371,9 +359,7 @@ class DeepLinkActivity : AppCompatActivity() {
|
||||
return false
|
||||
}
|
||||
|
||||
startActivity(
|
||||
CreatorChannelActivity.newIntent(applicationContext, deepLinkValueId)
|
||||
)
|
||||
handleCreatorAction(CreatorActionCommand.Profile(deepLinkValueId))
|
||||
true
|
||||
}
|
||||
|
||||
@@ -391,15 +377,13 @@ class DeepLinkActivity : AppCompatActivity() {
|
||||
return false
|
||||
}
|
||||
|
||||
startActivity(
|
||||
Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, deepLinkValueId)
|
||||
}
|
||||
)
|
||||
// deepLinkValueId는 legacy creatorId이며 postId는 routeForegroundDeepLink에서 먼저 처리한다.
|
||||
handleCreatorAction(CreatorActionCommand.Profile(deepLinkValueId))
|
||||
true
|
||||
}
|
||||
|
||||
"message" -> {
|
||||
// 현재 DM은 chat/{roomId} 계약을 사용하며 message는 legacy fallback이다.
|
||||
startActivity(Intent(applicationContext, MessageActivity::class.java))
|
||||
true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package kr.co.vividnext.sodalive.v2.chat.action
|
||||
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
|
||||
class ChatAction {
|
||||
fun execute(
|
||||
command: ChatActionCommand,
|
||||
ensureAccess: (AccessRequirement) -> Boolean
|
||||
): ChatActionResult {
|
||||
return when (command) {
|
||||
is ChatActionCommand.AiRoom -> {
|
||||
if (command.roomId <= 0L) return ChatActionResult.Ignored
|
||||
if (!ensureAccess(AccessRequirement.Login)) return ChatActionResult.Blocked(AccessRequirement.Login)
|
||||
ChatActionResult.NavigateToAiRoom(command.roomId)
|
||||
}
|
||||
is ChatActionCommand.DmRoom -> {
|
||||
if (command.roomId <= 0L) return ChatActionResult.Ignored
|
||||
if (!ensureAccess(AccessRequirement.Login)) return ChatActionResult.Blocked(AccessRequirement.Login)
|
||||
ChatActionResult.NavigateToDmRoom(command.roomId)
|
||||
}
|
||||
is ChatActionCommand.DmCreator -> {
|
||||
if (command.creatorId <= 0L) return ChatActionResult.Ignored
|
||||
if (!ensureAccess(AccessRequirement.Login)) return ChatActionResult.Blocked(AccessRequirement.Login)
|
||||
ChatActionResult.NavigateToCreatorDm(command.creatorId)
|
||||
}
|
||||
ChatActionCommand.OwnerDmList -> {
|
||||
if (!ensureAccess(AccessRequirement.Login)) return ChatActionResult.Blocked(AccessRequirement.Login)
|
||||
ChatActionResult.NavigateToOwnerDmList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface ChatActionResult {
|
||||
data object Ignored : ChatActionResult
|
||||
|
||||
data class Blocked(
|
||||
val requirement: AccessRequirement
|
||||
) : ChatActionResult
|
||||
|
||||
data class NavigateToAiRoom(
|
||||
val roomId: Long
|
||||
) : ChatActionResult
|
||||
|
||||
data class NavigateToDmRoom(
|
||||
val roomId: Long
|
||||
) : ChatActionResult
|
||||
|
||||
data class NavigateToCreatorDm(
|
||||
val creatorId: Long
|
||||
) : ChatActionResult
|
||||
|
||||
data object NavigateToOwnerDmList : ChatActionResult
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package kr.co.vividnext.sodalive.v2.chat.action
|
||||
|
||||
sealed interface ChatActionCommand {
|
||||
data class AiRoom(
|
||||
val roomId: Long
|
||||
) : ChatActionCommand
|
||||
|
||||
data class DmRoom(
|
||||
val roomId: Long
|
||||
) : ChatActionCommand
|
||||
|
||||
data class DmCreator(
|
||||
val creatorId: Long
|
||||
) : ChatActionCommand
|
||||
|
||||
data object OwnerDmList : ChatActionCommand
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package kr.co.vividnext.sodalive.v2.chat.action
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.access.ensureV2Access
|
||||
import kr.co.vividnext.sodalive.v2.main.MainV2Activity
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
|
||||
|
||||
internal class ChatActionHandler(
|
||||
private val action: ChatAction = ChatAction()
|
||||
) {
|
||||
@OptIn(UnstableApi::class)
|
||||
fun handle(
|
||||
context: Context,
|
||||
command: ChatActionCommand,
|
||||
ensureAccess: (AccessRequirement) -> Boolean
|
||||
): ChatActionResult {
|
||||
val result = action.execute(command, ensureAccess)
|
||||
when (result) {
|
||||
is ChatActionResult.NavigateToAiRoom -> context.startActivity(
|
||||
ChatRoomActivity.newIntent(context, result.roomId)
|
||||
)
|
||||
is ChatActionResult.NavigateToDmRoom -> context.startActivity(
|
||||
DmChatRoomActivity.newIntentByRoomId(context, result.roomId)
|
||||
)
|
||||
is ChatActionResult.NavigateToCreatorDm -> context.startActivity(
|
||||
DmChatRoomActivity.newIntentByCreatorId(context, result.creatorId)
|
||||
)
|
||||
ChatActionResult.NavigateToOwnerDmList -> context.startActivity(MainV2Activity.newChatDmIntent(context))
|
||||
is ChatActionResult.Blocked,
|
||||
ChatActionResult.Ignored -> Unit
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.handleChatAction(command: ChatActionCommand): ChatActionResult {
|
||||
return ChatActionHandler().handle(this, command, ::ensureV2Access)
|
||||
}
|
||||
|
||||
fun Fragment.handleChatAction(command: ChatActionCommand): ChatActionResult {
|
||||
return ChatActionHandler().handle(requireContext(), command, ::ensureV2Access)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package kr.co.vividnext.sodalive.v2.community.action
|
||||
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
|
||||
class CommunityAction {
|
||||
fun execute(
|
||||
command: CommunityActionCommand,
|
||||
ensureAccess: (AccessRequirement) -> Boolean
|
||||
): CommunityActionResult {
|
||||
val postId = when (command) {
|
||||
is CommunityActionCommand.PostDetail -> command.postId
|
||||
}
|
||||
if (postId <= 0L) return CommunityActionResult.Ignored
|
||||
|
||||
val requirement = AccessRequirement.Login
|
||||
if (!ensureAccess(requirement)) return CommunityActionResult.Blocked(requirement)
|
||||
|
||||
return when (command) {
|
||||
is CommunityActionCommand.PostDetail -> CommunityActionResult.NavigateToPostDetail(postId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface CommunityActionResult {
|
||||
data object Ignored : CommunityActionResult
|
||||
|
||||
data class Blocked(
|
||||
val requirement: AccessRequirement
|
||||
) : CommunityActionResult
|
||||
|
||||
data class NavigateToPostDetail(
|
||||
val postId: Long
|
||||
) : CommunityActionResult
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package kr.co.vividnext.sodalive.v2.community.action
|
||||
|
||||
sealed interface CommunityActionCommand {
|
||||
data class PostDetail(
|
||||
val postId: Long
|
||||
) : CommunityActionCommand
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package kr.co.vividnext.sodalive.v2.community.action
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.fragment.app.Fragment
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.access.ensureV2Access
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityDetailActivity
|
||||
|
||||
internal class CommunityActionHandler(
|
||||
private val action: CommunityAction = CommunityAction()
|
||||
) {
|
||||
fun handle(
|
||||
context: Context,
|
||||
command: CommunityActionCommand,
|
||||
ensureAccess: (AccessRequirement) -> Boolean,
|
||||
launchIntent: ((Intent) -> Unit)? = null
|
||||
): CommunityActionResult {
|
||||
val result = action.execute(command, ensureAccess)
|
||||
if (result is CommunityActionResult.NavigateToPostDetail) {
|
||||
val intent = CreatorChannelCommunityDetailActivity.newIntent(context, result.postId)
|
||||
if (launchIntent != null) {
|
||||
launchIntent(intent)
|
||||
} else {
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.handleCommunityAction(
|
||||
command: CommunityActionCommand,
|
||||
launchIntent: ((Intent) -> Unit)? = null
|
||||
): CommunityActionResult {
|
||||
return CommunityActionHandler().handle(this, command, ::ensureV2Access, launchIntent)
|
||||
}
|
||||
|
||||
fun Fragment.handleCommunityAction(command: CommunityActionCommand): CommunityActionResult {
|
||||
return CommunityActionHandler().handle(requireContext(), command, ::ensureV2Access)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package kr.co.vividnext.sodalive.v2.community.action
|
||||
|
||||
import android.app.Activity
|
||||
|
||||
enum class CommunityActivityResultSource {
|
||||
Write,
|
||||
Modify,
|
||||
Detail
|
||||
}
|
||||
|
||||
fun resolveCommunityActivityResult(
|
||||
source: CommunityActivityResultSource,
|
||||
resultCode: Int
|
||||
): CommunityChange {
|
||||
if (resultCode != Activity.RESULT_OK) return CommunityChange.Ignored
|
||||
return when (source) {
|
||||
CommunityActivityResultSource.Write -> CommunityChange.Created
|
||||
CommunityActivityResultSource.Modify,
|
||||
CommunityActivityResultSource.Detail -> CommunityChange.Updated
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package kr.co.vividnext.sodalive.v2.community.action
|
||||
|
||||
sealed interface CommunityChange {
|
||||
data object Ignored : CommunityChange
|
||||
|
||||
data object Created : CommunityChange
|
||||
|
||||
data object Updated : CommunityChange
|
||||
|
||||
data class Deleted(
|
||||
val postId: Long
|
||||
) : CommunityChange
|
||||
|
||||
data class PinChanged(
|
||||
val postId: Long
|
||||
) : CommunityChange
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.action
|
||||
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
|
||||
class CreatorAction {
|
||||
fun execute(
|
||||
command: CreatorActionCommand,
|
||||
ensureAccess: (AccessRequirement) -> Boolean
|
||||
): CreatorActionResult {
|
||||
val creatorId = when (command) {
|
||||
is CreatorActionCommand.Profile -> command.creatorId
|
||||
}
|
||||
if (creatorId <= 0L) return CreatorActionResult.Ignored
|
||||
|
||||
val requirement = AccessRequirement.Login
|
||||
if (!ensureAccess(requirement)) return CreatorActionResult.Blocked(requirement)
|
||||
|
||||
return when (command) {
|
||||
is CreatorActionCommand.Profile -> CreatorActionResult.NavigateToCreatorProfile(creatorId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface CreatorActionResult {
|
||||
data object Ignored : CreatorActionResult
|
||||
|
||||
data class Blocked(
|
||||
val requirement: AccessRequirement
|
||||
) : CreatorActionResult
|
||||
|
||||
data class NavigateToCreatorProfile(
|
||||
val creatorId: Long
|
||||
) : CreatorActionResult
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.action
|
||||
|
||||
sealed interface CreatorActionCommand {
|
||||
data class Profile(
|
||||
val creatorId: Long
|
||||
) : CreatorActionCommand
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.action
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.access.ensureV2Access
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
|
||||
internal class CreatorActionHandler(
|
||||
private val action: CreatorAction = CreatorAction()
|
||||
) {
|
||||
fun handle(
|
||||
context: Context,
|
||||
command: CreatorActionCommand,
|
||||
ensureAccess: (AccessRequirement) -> Boolean
|
||||
): CreatorActionResult {
|
||||
val result = action.execute(command, ensureAccess)
|
||||
if (result is CreatorActionResult.NavigateToCreatorProfile) {
|
||||
context.startActivity(CreatorChannelActivity.newIntent(context, result.creatorId))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.handleCreatorAction(command: CreatorActionCommand): CreatorActionResult {
|
||||
return CreatorActionHandler().handle(this, command, ::ensureV2Access)
|
||||
}
|
||||
|
||||
fun Fragment.handleCreatorAction(command: CreatorActionCommand): CreatorActionResult {
|
||||
return CreatorActionHandler().handle(requireContext(), command, ::ensureV2Access)
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
@@ -47,13 +46,19 @@ import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationDialog
|
||||
import kr.co.vividnext.sodalive.report.UserReportDialog
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.access.ensureV2Access
|
||||
import kr.co.vividnext.sodalive.v2.chat.action.ChatActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.chat.action.handleChatAction
|
||||
import kr.co.vividnext.sodalive.v2.community.action.CommunityActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.community.action.CommunityActivityResultSource
|
||||
import kr.co.vividnext.sodalive.v2.community.action.CommunityChange
|
||||
import kr.co.vividnext.sodalive.v2.community.action.handleCommunityAction
|
||||
import kr.co.vividnext.sodalive.v2.community.action.resolveCommunityActivityResult
|
||||
import kr.co.vividnext.sodalive.v2.content.action.ContentActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.content.action.handleContentAction
|
||||
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
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragment
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityDetailActivity
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityPostUiModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelAudioContentResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelLiveResponse
|
||||
@@ -74,8 +79,6 @@ import kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesFr
|
||||
import kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinator
|
||||
import kr.co.vividnext.sodalive.v2.live.action.LiveCreationResult
|
||||
import kr.co.vividnext.sodalive.v2.live.action.resolveLiveCreationResult
|
||||
import kr.co.vividnext.sodalive.v2.main.MainV2Activity
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.koin.android.ext.android.inject
|
||||
@@ -118,24 +121,23 @@ class CreatorChannelActivity :
|
||||
private val communityWriteLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
homeActionDelegate?.refreshHome()
|
||||
refreshCreatorChannelCommunity()
|
||||
}
|
||||
handleCommunityChange(
|
||||
resolveCommunityActivityResult(CommunityActivityResultSource.Write, result.resultCode)
|
||||
)
|
||||
}
|
||||
private val communityPostModifyLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
refreshCreatorChannelCommunity()
|
||||
}
|
||||
handleCommunityChange(
|
||||
resolveCommunityActivityResult(CommunityActivityResultSource.Modify, result.resultCode)
|
||||
)
|
||||
}
|
||||
private val communityDetailLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
refreshCreatorChannelCommunity()
|
||||
}
|
||||
handleCommunityChange(
|
||||
resolveCommunityActivityResult(CommunityActivityResultSource.Detail, result.resultCode)
|
||||
)
|
||||
}
|
||||
private val fanTalkWriteLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
@@ -242,9 +244,9 @@ class CreatorChannelActivity :
|
||||
}
|
||||
binding.tvDmButton.setOnClickListener {
|
||||
if (currentHeader?.isOwner == true) {
|
||||
startActivity(MainV2Activity.newChatDmIntent(this))
|
||||
handleChatAction(ChatActionCommand.OwnerDmList)
|
||||
} else {
|
||||
startActivity(DmChatRoomActivity.newIntentByCreatorId(this, creatorId))
|
||||
handleChatAction(ChatActionCommand.DmCreator(creatorId))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -538,7 +540,7 @@ class CreatorChannelActivity :
|
||||
}
|
||||
|
||||
override fun onCreatorChannelChatRoomCreated(chatRoomId: Long) {
|
||||
startActivity(ChatRoomActivity.newIntent(this, chatRoomId))
|
||||
handleChatAction(ChatActionCommand.AiRoom(chatRoomId))
|
||||
}
|
||||
|
||||
override fun onCreatorChannelScheduleClicked(schedule: CreatorChannelScheduleResponse) {
|
||||
@@ -612,10 +614,7 @@ class CreatorChannelActivity :
|
||||
}
|
||||
|
||||
override fun onCreatorChannelCommunityPostClicked(postId: Long) {
|
||||
if (postId <= 0L) return
|
||||
ensureV2Access(AccessRequirement.Login) {
|
||||
communityDetailLauncher.launch(CreatorChannelCommunityDetailActivity.newIntent(this, postId))
|
||||
}
|
||||
handleCommunityAction(CommunityActionCommand.PostDetail(postId), communityDetailLauncher::launch)
|
||||
}
|
||||
|
||||
override fun onCreatorChannelFanTalkContentChanged() {
|
||||
@@ -736,7 +735,7 @@ class CreatorChannelActivity :
|
||||
.subscribe(
|
||||
{ response ->
|
||||
if (response.success) {
|
||||
refreshCreatorChannelCommunity()
|
||||
handleCommunityChange(CommunityChange.PinChanged(item.postId))
|
||||
} else {
|
||||
response.message?.let(::showToast)
|
||||
}
|
||||
@@ -763,7 +762,7 @@ class CreatorChannelActivity :
|
||||
.subscribe(
|
||||
{ response ->
|
||||
if (response.success) {
|
||||
refreshCreatorChannelCommunity()
|
||||
handleCommunityChange(CommunityChange.Deleted(item.postId))
|
||||
} else {
|
||||
response.message?.let(::showToast)
|
||||
}
|
||||
@@ -779,6 +778,19 @@ class CreatorChannelActivity :
|
||||
findCommunityFragment()?.onCreatorChannelCommunityRefreshRequested()
|
||||
}
|
||||
|
||||
private fun handleCommunityChange(change: CommunityChange) {
|
||||
when (change) {
|
||||
CommunityChange.Ignored -> Unit
|
||||
CommunityChange.Created -> {
|
||||
homeActionDelegate?.refreshHome()
|
||||
refreshCreatorChannelCommunity()
|
||||
}
|
||||
CommunityChange.Updated,
|
||||
is CommunityChange.Deleted,
|
||||
is CommunityChange.PinChanged -> refreshCreatorChannelCommunity()
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshCreatorChannelFanTalk() {
|
||||
findFanTalkFragment()?.onCreatorChannelFanTalkRefreshRequested()
|
||||
}
|
||||
|
||||
@@ -37,14 +37,14 @@ import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
|
||||
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
|
||||
import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerFragment
|
||||
import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerService
|
||||
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
|
||||
import kr.co.vividnext.sodalive.audition.AuditionActivity
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityMainV2Binding
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.live.LiveViewModel
|
||||
import kr.co.vividnext.sodalive.main.EventPopupDialogFragment
|
||||
import kr.co.vividnext.sodalive.message.MessageActivity
|
||||
import kr.co.vividnext.sodalive.mypage.MyPageFragment
|
||||
@@ -53,10 +53,17 @@ import kr.co.vividnext.sodalive.settings.notification.NotificationSettingsDialog
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.access.ensureV2Access
|
||||
import kr.co.vividnext.sodalive.v2.access.isV2AccessAllowed
|
||||
import kr.co.vividnext.sodalive.v2.chat.action.ChatActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.chat.action.handleChatAction
|
||||
import kr.co.vividnext.sodalive.v2.common.data.ContentSort
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
import kr.co.vividnext.sodalive.v2.community.action.CommunityActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.community.action.handleCommunityAction
|
||||
import kr.co.vividnext.sodalive.v2.content.action.ContentActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.content.action.handleContentAction
|
||||
import kr.co.vividnext.sodalive.v2.creator.action.CreatorActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.creator.action.handleCreatorAction
|
||||
import kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinator
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.ChatMainFragment
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.model.ChatRoomFilter
|
||||
import kr.co.vividnext.sodalive.v2.main.content.ContentMainFragment
|
||||
import kr.co.vividnext.sodalive.v2.main.content.data.MainContentAllType
|
||||
@@ -72,19 +79,37 @@ import kotlin.math.max
|
||||
class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding::inflate) {
|
||||
|
||||
private val viewModel: MainV2ViewModel by inject()
|
||||
private val liveViewModel: LiveViewModel by inject()
|
||||
private val liveActionCoordinator: LiveActionCoordinator by lazy {
|
||||
LiveActionCoordinator(
|
||||
activity = this,
|
||||
layoutInflater = layoutInflater,
|
||||
fragmentManager = supportFragmentManager,
|
||||
liveViewModel = liveViewModel,
|
||||
screenWidthProvider = { screenWidth },
|
||||
refreshHome = {}
|
||||
)
|
||||
}
|
||||
|
||||
private lateinit var notificationSettingsDialog: NotificationSettingsDialog
|
||||
private lateinit var routeLoadingDialog: LoadingDialog
|
||||
private var mediaController: MediaController? = null
|
||||
private var mediaControllerFuture: ListenableFuture<MediaController>? = null
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val showMiniPlayerRunnable = Runnable { initAndVisibleMiniPlayer() }
|
||||
private val audioContentReceiver = AudioContentReceiver()
|
||||
private var playerStateJob: Job? = null
|
||||
private var isDeferredRouteLoading = false
|
||||
private var isLiveEntryLoading = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
overrideRootWindowInsets()
|
||||
|
||||
isDeferredRouteLoading =
|
||||
intent.hasExtra(Constants.EXTRA_DATA) || intent.hasExtra(EXTRA_AUDIO_NOTIFICATION_ROUTE)
|
||||
updateRouteLoadingDialog()
|
||||
|
||||
checkPermissions()
|
||||
trackAppLaunchIfNeeded()
|
||||
pushTokenUpdate()
|
||||
@@ -93,6 +118,8 @@ class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding
|
||||
if (!handleAudioNotificationRoute(intent) && isV2AccessAllowed(AccessRequirement.Login)) {
|
||||
executeDeeplink(intent)
|
||||
}
|
||||
isDeferredRouteLoading = false
|
||||
updateRouteLoadingDialog()
|
||||
}, 1000)
|
||||
|
||||
if (isV2AccessAllowed(AccessRequirement.Login)) {
|
||||
@@ -172,12 +199,32 @@ class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding
|
||||
)
|
||||
}
|
||||
|
||||
setupLiveEntryObservers()
|
||||
setupBottomNavigation()
|
||||
if (intent.hasExtra(EXTRA_CHAT_FILTER)) {
|
||||
selectChatTabWithLoginGuard()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupLiveEntryObservers() {
|
||||
routeLoadingDialog = LoadingDialog(this, layoutInflater)
|
||||
liveViewModel.toastLiveData.observe(this) {
|
||||
it?.let(::showToast)
|
||||
}
|
||||
liveViewModel.isLoading.observe(this) { isLoading ->
|
||||
isLiveEntryLoading = isLoading
|
||||
updateRouteLoadingDialog()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRouteLoadingDialog() {
|
||||
if (isDeferredRouteLoading || isLiveEntryLoading) {
|
||||
routeLoadingDialog.show(screenWidth)
|
||||
} else {
|
||||
routeLoadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
fun openChatTab() {
|
||||
selectChatTabWithLoginGuard()
|
||||
}
|
||||
@@ -433,15 +480,7 @@ class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding
|
||||
val contentId = intent.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)
|
||||
intent.removeExtra(Constants.EXTRA_AUDIO_CONTENT_ID)
|
||||
if (contentId > 0) {
|
||||
ensureV2Access(AccessRequirement.Login) {
|
||||
startActivity(
|
||||
Intent(applicationContext, AudioContentDetailActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId)
|
||||
}
|
||||
)
|
||||
}
|
||||
} else if (!isV2AccessAllowed(AccessRequirement.Login)) {
|
||||
ensureV2Access(AccessRequirement.Login)
|
||||
handleContentAction(ContentActionCommand.AudioDetail(contentId))
|
||||
}
|
||||
contentId > 0
|
||||
}
|
||||
@@ -566,9 +605,16 @@ class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding
|
||||
putQuery("content_id")
|
||||
putQuery("deep_link_value")
|
||||
putQuery("deep_link_sub5")
|
||||
putQuery("postId")
|
||||
putQuery(Constants.EXTRA_COMMUNITY_CREATOR_ID)
|
||||
putQuery(Constants.EXTRA_COMMUNITY_POST_ID)
|
||||
|
||||
extras.getString("postId")?.takeIf { it.isNotBlank() }?.let {
|
||||
if (!extras.containsKey(Constants.EXTRA_COMMUNITY_POST_ID)) {
|
||||
extras.putString(Constants.EXTRA_COMMUNITY_POST_ID, it)
|
||||
}
|
||||
}
|
||||
|
||||
applyPathDeepLink(data = data) { key, value ->
|
||||
if (!value.isNullOrBlank() && !extras.containsKey(key)) {
|
||||
extras.putString(key, value)
|
||||
@@ -652,41 +698,39 @@ class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding
|
||||
?: bundle.getLong(Constants.EXTRA_COMMUNITY_POST_ID).takeIf { it > 0 }
|
||||
|
||||
if (isDmChatDeepLink(bundle) && roomId != null && roomId > 0) {
|
||||
startActivity(DmChatRoomActivity.newIntentByRoomId(applicationContext, roomId))
|
||||
handleChatAction(ChatActionCommand.DmRoom(roomId))
|
||||
return true
|
||||
}
|
||||
|
||||
when {
|
||||
roomId != null && roomId > 0 -> {
|
||||
liveActionCoordinator.enterLiveRoom(roomId)
|
||||
return true
|
||||
}
|
||||
|
||||
communityPostId != null && communityPostId > 0 -> {
|
||||
handleCommunityAction(CommunityActionCommand.PostDetail(communityPostId))
|
||||
return true
|
||||
}
|
||||
|
||||
channelId != null && channelId > 0 -> {
|
||||
startActivity(
|
||||
CreatorChannelActivity.newIntent(applicationContext, channelId)
|
||||
)
|
||||
handleCreatorAction(CreatorActionCommand.Profile(channelId))
|
||||
return true
|
||||
}
|
||||
|
||||
contentId != null && contentId > 0 -> {
|
||||
startActivity(
|
||||
Intent(applicationContext, AudioContentDetailActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId)
|
||||
}
|
||||
)
|
||||
handleContentAction(ContentActionCommand.AudioDetail(contentId))
|
||||
return true
|
||||
}
|
||||
|
||||
messageId != null && messageId > 0 -> {
|
||||
// messageId는 DM room ID가 아니므로 과거 알림 수신 호환만 유지한다.
|
||||
startActivity(Intent(applicationContext, MessageActivity::class.java))
|
||||
return true
|
||||
}
|
||||
|
||||
communityCreatorId != null && communityCreatorId > 0 -> {
|
||||
startActivity(
|
||||
Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, communityCreatorId)
|
||||
if (communityPostId != null && communityPostId > 0) {
|
||||
putExtra(Constants.EXTRA_COMMUNITY_POST_ID, communityPostId)
|
||||
}
|
||||
}
|
||||
)
|
||||
handleCreatorAction(CreatorActionCommand.Profile(communityCreatorId))
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -711,11 +755,7 @@ class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding
|
||||
if (deepLinkValueId == null || deepLinkValueId <= 0) {
|
||||
return false
|
||||
}
|
||||
startActivity(
|
||||
Intent(applicationContext, SeriesDetailActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_SERIES_ID, deepLinkValueId)
|
||||
}
|
||||
)
|
||||
handleContentAction(ContentActionCommand.SeriesDetail(deepLinkValueId))
|
||||
true
|
||||
}
|
||||
|
||||
@@ -723,11 +763,7 @@ class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding
|
||||
if (deepLinkValueId == null || deepLinkValueId <= 0) {
|
||||
return false
|
||||
}
|
||||
startActivity(
|
||||
Intent(applicationContext, AudioContentDetailActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, deepLinkValueId)
|
||||
}
|
||||
)
|
||||
handleContentAction(ContentActionCommand.AudioDetail(deepLinkValueId))
|
||||
true
|
||||
}
|
||||
|
||||
@@ -735,9 +771,15 @@ class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding
|
||||
if (deepLinkValueId == null || deepLinkValueId <= 0) {
|
||||
return false
|
||||
}
|
||||
startActivity(
|
||||
CreatorChannelActivity.newIntent(applicationContext, deepLinkValueId)
|
||||
)
|
||||
handleCreatorAction(CreatorActionCommand.Profile(deepLinkValueId))
|
||||
true
|
||||
}
|
||||
|
||||
"live" -> {
|
||||
if (deepLinkValueId == null || deepLinkValueId <= 0) {
|
||||
return false
|
||||
}
|
||||
liveActionCoordinator.enterLiveRoom(deepLinkValueId)
|
||||
true
|
||||
}
|
||||
|
||||
@@ -745,15 +787,13 @@ class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding
|
||||
if (deepLinkValueId == null || deepLinkValueId <= 0) {
|
||||
return false
|
||||
}
|
||||
startActivity(
|
||||
Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, deepLinkValueId)
|
||||
}
|
||||
)
|
||||
// deepLinkValueId는 legacy creatorId이며 postId는 executeBundleRoute에서 먼저 처리한다.
|
||||
handleCreatorAction(CreatorActionCommand.Profile(deepLinkValueId))
|
||||
true
|
||||
}
|
||||
|
||||
"message" -> {
|
||||
// 현재 DM은 chat/{roomId} 계약을 사용하며 message는 legacy fallback이다.
|
||||
startActivity(Intent(applicationContext, MessageActivity::class.java))
|
||||
true
|
||||
}
|
||||
|
||||
@@ -11,14 +11,14 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.base.BaseFragment
|
||||
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.databinding.FragmentV2MainChatBinding
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
|
||||
import kr.co.vividnext.sodalive.search.SearchActivity
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.access.ensureV2Access
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.v2.chat.action.ChatActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.chat.action.handleChatAction
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.model.ChatRoomFilter
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.model.ChatRoomListUiItem
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.model.ChatRoomListUiState
|
||||
@@ -173,8 +173,8 @@ class ChatMainFragment : BaseFragment<FragmentV2MainChatBinding>(
|
||||
|
||||
private fun onChatRoomClick(item: ChatRoomListUiItem) {
|
||||
when (item.chatType) {
|
||||
ChatRoomType.AI -> startActivity(ChatRoomActivity.newIntent(requireContext(), item.roomId))
|
||||
ChatRoomType.DM -> startActivity(DmChatRoomActivity.newIntentByRoomId(requireContext(), item.roomId))
|
||||
ChatRoomType.AI -> handleChatAction(ChatActionCommand.AiRoom(item.roomId))
|
||||
ChatRoomType.DM -> handleChatAction(ChatActionCommand.DmRoom(item.roomId))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.access.ensureV2Access
|
||||
import kr.co.vividnext.sodalive.v2.content.action.ContentActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.content.action.handleContentAction
|
||||
import kr.co.vividnext.sodalive.v2.creator.action.CreatorActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.creator.action.handleCreatorAction
|
||||
import kr.co.vividnext.sodalive.v2.main.content.data.AudioRankingType
|
||||
import kr.co.vividnext.sodalive.v2.main.content.data.MainContentAllType
|
||||
import kr.co.vividnext.sodalive.v2.main.content.model.AudioRankingsUiState
|
||||
@@ -617,6 +619,9 @@ class ContentMainFragment : BaseFragment<FragmentV2MainContentBinding>(
|
||||
private fun onBannerClick(item: ContentBannerUiModel) {
|
||||
val route = item.toContentBannerRoute() ?: return
|
||||
when (route) {
|
||||
is ContentBannerRoute.Creator -> handleCreatorAction(
|
||||
CreatorActionCommand.Profile(route.creatorId)
|
||||
)
|
||||
is ContentBannerRoute.Series -> handleContentAction(
|
||||
ContentActionCommand.SeriesDetail(seriesId = route.seriesId)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,6 @@ import kr.co.vividnext.sodalive.BuildConfig
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
|
||||
import kr.co.vividnext.sodalive.settings.event.EventItem
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
import kr.co.vividnext.sodalive.v2.widget.AudioContentTag
|
||||
|
||||
data class ContentBannerSection(
|
||||
@@ -68,7 +67,7 @@ fun ContentBannerRoute.toContentBannerIntent(context: Context): Intent? {
|
||||
putExtra(Constants.EXTRA_EVENT, eventItem)
|
||||
}
|
||||
|
||||
is ContentBannerRoute.Creator -> CreatorChannelActivity.newIntent(context, creatorId)
|
||||
is ContentBannerRoute.Creator -> null
|
||||
|
||||
is ContentBannerRoute.Series -> null
|
||||
|
||||
|
||||
@@ -6,28 +6,30 @@ import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.base.BaseFragment
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.ToastMessage
|
||||
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||
import kr.co.vividnext.sodalive.databinding.FragmentV2MainHomeBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ViewSectionTitleBinding
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.following.FollowingCreatorActivity
|
||||
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.home.pushnotification.PushNotificationListActivity
|
||||
import kr.co.vividnext.sodalive.live.LiveViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
|
||||
import kr.co.vividnext.sodalive.search.SearchActivity
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityDetailActivity
|
||||
import kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveActivity
|
||||
import kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinator
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.model.ChatRoomListUiItem
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.model.ChatRoomType
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.access.ensureV2Access
|
||||
import kr.co.vividnext.sodalive.v2.chat.action.ChatActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.chat.action.handleChatAction
|
||||
import kr.co.vividnext.sodalive.v2.community.action.CommunityActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.community.action.handleCommunityAction
|
||||
import kr.co.vividnext.sodalive.v2.content.action.ContentActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.content.action.handleContentAction
|
||||
import kr.co.vividnext.sodalive.v2.creator.action.CreatorActionCommand
|
||||
import kr.co.vividnext.sodalive.v2.creator.action.handleCreatorAction
|
||||
import kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinator
|
||||
import kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveActivity
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.model.ChatRoomListUiItem
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.model.ChatRoomType
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingChatSection
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingCreatorSection
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingLiveSection
|
||||
@@ -45,6 +47,7 @@ import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationBannerRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationBannerUiModel
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationCheerCreatorSection
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationGenreCreatorSection
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationAiCharacterRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationLiveSection
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationLiveUiModel
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationPopularCommunityPostSection
|
||||
@@ -56,7 +59,6 @@ import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationRecentlyAct
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationUiState
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationBannerIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationBannerRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationAiCharacterIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationAiCharacterRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationRecentlyActiveCreatorRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeAiCharacterAdapter
|
||||
@@ -556,9 +558,7 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
||||
val community = item as? HomeFollowingNewsUiItem.Community ?: return
|
||||
val postId = community.postId
|
||||
if (postId <= 0L) return
|
||||
ensureV2Access(AccessRequirement.Login) {
|
||||
startActivity(CreatorChannelCommunityDetailActivity.newIntent(requireContext(), postId))
|
||||
}
|
||||
handleCommunityAction(CommunityActionCommand.PostDetail(postId))
|
||||
}
|
||||
|
||||
private fun openFollowingCreatorAll() {
|
||||
@@ -568,17 +568,18 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
||||
}
|
||||
|
||||
private fun openFollowingChat(item: ChatRoomListUiItem) {
|
||||
ensureV2Access(AccessRequirement.Login) {
|
||||
when (item.chatType) {
|
||||
ChatRoomType.AI -> startActivity(ChatRoomActivity.newIntent(requireContext(), item.roomId))
|
||||
ChatRoomType.DM -> startActivity(DmChatRoomActivity.newIntentByRoomId(requireContext(), item.roomId))
|
||||
}
|
||||
when (item.chatType) {
|
||||
ChatRoomType.AI -> handleChatAction(ChatActionCommand.AiRoom(item.roomId))
|
||||
ChatRoomType.DM -> handleChatAction(ChatActionCommand.DmRoom(item.roomId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBannerClick(item: HomeRecommendationBannerUiModel) {
|
||||
val route = item.toHomeRecommendationBannerRoute() ?: return
|
||||
when (route) {
|
||||
is HomeRecommendationBannerRoute.Creator -> handleCreatorAction(
|
||||
CreatorActionCommand.Profile(route.creatorId)
|
||||
)
|
||||
is HomeRecommendationBannerRoute.Series -> handleContentAction(
|
||||
ContentActionCommand.SeriesDetail(seriesId = route.seriesId)
|
||||
)
|
||||
@@ -594,16 +595,18 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
||||
is HomeRecommendationRecentlyActiveCreatorRoute.AudioContent -> handleContentAction(
|
||||
ContentActionCommand.AudioDetail(audioContentId = route.contentId)
|
||||
)
|
||||
is HomeRecommendationRecentlyActiveCreatorRoute.Community -> ensureV2Access(AccessRequirement.Login) {
|
||||
startActivity(CreatorChannelCommunityDetailActivity.newIntent(requireContext(), route.postId))
|
||||
}
|
||||
is HomeRecommendationRecentlyActiveCreatorRoute.Community -> handleCommunityAction(
|
||||
CommunityActionCommand.PostDetail(route.postId)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAiCharacterClick(item: HomeRecommendationAiCharacterUiModel) {
|
||||
val route = item.toHomeRecommendationAiCharacterRoute() ?: return
|
||||
ensureV2Access(AccessRequirement.Login) {
|
||||
startActivity(route.toHomeRecommendationAiCharacterIntent(requireContext()))
|
||||
when (route) {
|
||||
is HomeRecommendationAiCharacterRoute.Creator -> handleCreatorAction(
|
||||
CreatorActionCommand.Profile(route.creatorId)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,18 +616,12 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
||||
}
|
||||
|
||||
private fun openCreatorProfile(creatorId: Long) {
|
||||
ensureV2Access(AccessRequirement.Login) {
|
||||
startActivity(
|
||||
CreatorChannelActivity.newIntent(requireContext(), creatorId)
|
||||
)
|
||||
}
|
||||
handleCreatorAction(CreatorActionCommand.Profile(creatorId))
|
||||
}
|
||||
|
||||
private fun openPopularCommunityPost(item: FeedItem.Community) {
|
||||
val postId = item.postId.toLongOrNull() ?: return
|
||||
ensureV2Access(AccessRequirement.Login) {
|
||||
startActivity(CreatorChannelCommunityDetailActivity.newIntent(requireContext(), postId))
|
||||
}
|
||||
handleCommunityAction(CommunityActionCommand.PostDetail(postId))
|
||||
}
|
||||
|
||||
private fun showToast(toastMessage: ToastMessage) {
|
||||
|
||||
@@ -8,10 +8,10 @@ import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
|
||||
import kr.co.vividnext.sodalive.settings.event.EventItem
|
||||
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
import kr.co.vividnext.sodalive.v2.widget.characterchatthumbnail.CharacterChatThumbnailItem
|
||||
import kr.co.vividnext.sodalive.v2.widget.feed.FeedItem
|
||||
import java.util.Locale
|
||||
import androidx.core.net.toUri
|
||||
|
||||
data class HomeRecommendationLiveSection(
|
||||
val items: List<HomeRecommendationLiveUiModel>
|
||||
@@ -105,13 +105,11 @@ fun HomeRecommendationBannerRoute.toHomeRecommendationBannerIntent(context: Cont
|
||||
}
|
||||
}
|
||||
|
||||
is HomeRecommendationBannerRoute.Creator -> {
|
||||
CreatorChannelActivity.newIntent(context, creatorId)
|
||||
}
|
||||
is HomeRecommendationBannerRoute.Creator -> null
|
||||
|
||||
is HomeRecommendationBannerRoute.Series -> null
|
||||
|
||||
is HomeRecommendationBannerRoute.Link -> Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
is HomeRecommendationBannerRoute.Link -> Intent(Intent.ACTION_VIEW, url.toUri())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,12 +161,6 @@ fun HomeRecommendationAiCharacterUiModel.toHomeRecommendationAiCharacterRoute():
|
||||
return creatorId.takeIf { it > 0L }?.let(HomeRecommendationAiCharacterRoute::Creator)
|
||||
}
|
||||
|
||||
fun HomeRecommendationAiCharacterRoute.toHomeRecommendationAiCharacterIntent(context: Context): Intent {
|
||||
return when (this) {
|
||||
is HomeRecommendationAiCharacterRoute.Creator -> CreatorChannelActivity.newIntent(context, creatorId)
|
||||
}
|
||||
}
|
||||
|
||||
data class HomeRecommendationGenreCreatorGroupUiModel(
|
||||
val genre: String,
|
||||
val creators: List<HomeRecommendationCreatorUiModel>,
|
||||
|
||||
@@ -8,14 +8,16 @@ import java.io.File
|
||||
class DeepLinkActivitySourceTest {
|
||||
|
||||
@Test
|
||||
fun `DeepLinkActivity는 chat_type 없이 chat path만 DM 채팅방으로 라우팅한다`() {
|
||||
fun `DeepLinkActivity는 chat path DM을 Chat Action으로 라우팅한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/main/DeepLinkActivity.kt").readText()
|
||||
|
||||
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity"))
|
||||
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.chat.action.ChatActionCommand"))
|
||||
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.chat.action.handleChatAction"))
|
||||
assertFalse(source.contains("chat_type"))
|
||||
assertFalse(source.contains("isUserCreatorChat"))
|
||||
assertTrue(source.contains("return bundle.getString(\"deep_link_value\") == \"chat\""))
|
||||
assertTrue(source.contains("DmChatRoomActivity.newIntentByRoomId(applicationContext, roomId)"))
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.DmRoom(roomId))"))
|
||||
assertFalse(source.contains("DmChatRoomActivity.newIntentByRoomId(applicationContext, roomId)"))
|
||||
assertTrue(source.contains("if (isDmChatDeepLink(bundle) && roomId != null && roomId > 0)"))
|
||||
}
|
||||
|
||||
@@ -50,6 +52,47 @@ class DeepLinkActivitySourceTest {
|
||||
assertTrue(dmRouteIndex < liveRouteIndex)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DeepLinkActivity는 Community post를 creator fallback보다 먼저 Action으로 라우팅한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/main/DeepLinkActivity.kt").readText()
|
||||
|
||||
val postRouteIndex = source.indexOf("communityPostId != null && communityPostId > 0 ->")
|
||||
val creatorFallbackIndex = source.indexOf("communityCreatorId != null && communityCreatorId > 0 ->")
|
||||
|
||||
assertTrue(postRouteIndex >= 0)
|
||||
assertTrue(creatorFallbackIndex >= 0)
|
||||
assertTrue(postRouteIndex < creatorFallbackIndex)
|
||||
assertTrue(
|
||||
source.contains(
|
||||
"handleCommunityAction(CommunityActionCommand.PostDetail(communityPostId))"
|
||||
)
|
||||
)
|
||||
assertTrue(
|
||||
source.contains(
|
||||
"handleCreatorAction(CreatorActionCommand.Profile(communityCreatorId))"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DeepLinkActivity는 foreground 도메인 상세를 기존 Action으로 실행한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/main/DeepLinkActivity.kt").readText()
|
||||
val routeSource = source.substringAfter("private fun routeByDeepLinkValue(")
|
||||
.substringBefore("private fun applyPathDeepLink(")
|
||||
val communityRoute = routeSource.substringAfter("\"community\" ->").substringBefore("\"message\" ->")
|
||||
val messageRoute = routeSource.substringAfter("\"message\" ->").substringBefore("\"audition\" ->")
|
||||
|
||||
assertTrue(source.contains("handleContentAction(ContentActionCommand.AudioDetail(contentId))"))
|
||||
assertTrue(source.contains("handleContentAction(ContentActionCommand.SeriesDetail(deepLinkValueId))"))
|
||||
assertTrue(source.contains("handleCreatorAction(CreatorActionCommand.Profile(channelId))"))
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.DmRoom(roomId))"))
|
||||
assertTrue(source.contains("routeLiveInMain(roomId)"))
|
||||
assertTrue(communityRoute.contains("handleCreatorAction(CreatorActionCommand.Profile(deepLinkValueId))"))
|
||||
assertFalse(communityRoute.contains("CommunityActionCommand.PostDetail"))
|
||||
assertTrue(messageRoute.contains("MessageActivity::class.java"))
|
||||
assertFalse(messageRoute.contains("ChatActionCommand.DmRoom"))
|
||||
}
|
||||
|
||||
private fun projectFile(relativePath: String): File {
|
||||
val candidates = listOf(File(relativePath), File("../$relativePath"))
|
||||
return candidates.firstOrNull { it.exists() }
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
package kr.co.vividnext.sodalive.v2.chat.action
|
||||
|
||||
import android.app.Activity
|
||||
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.main.MainV2Activity
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.Shadows.shadowOf
|
||||
import org.robolectric.annotation.Config
|
||||
import java.io.File
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(application = android.app.Application::class)
|
||||
class ChatActionTest {
|
||||
|
||||
private val action = ChatAction()
|
||||
|
||||
@Test
|
||||
fun `유효하지 않은 room 또는 creator ID는 무시한다`() {
|
||||
var accessCalls = 0
|
||||
val results = listOf(
|
||||
action.execute(ChatActionCommand.AiRoom(roomId = 0L)) { accessCalls += 1; true },
|
||||
action.execute(ChatActionCommand.DmRoom(roomId = -1L)) { accessCalls += 1; true },
|
||||
action.execute(ChatActionCommand.DmCreator(creatorId = 0L)) { accessCalls += 1; true }
|
||||
)
|
||||
|
||||
assertEquals(List(3) { ChatActionResult.Ignored }, results)
|
||||
assertEquals(0, accessCalls)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AI room command는 AI 채팅방 navigation 결과를 반환한다`() {
|
||||
val result = action.execute(ChatActionCommand.AiRoom(roomId = 41L)) { requirement ->
|
||||
assertEquals(AccessRequirement.Login, requirement)
|
||||
true
|
||||
}
|
||||
|
||||
assertEquals(ChatActionResult.NavigateToAiRoom(roomId = 41L), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DM command는 room과 creator 진입을 구분한다`() {
|
||||
val dmRoom = action.execute(ChatActionCommand.DmRoom(roomId = 42L)) { requirement ->
|
||||
assertEquals(AccessRequirement.Login, requirement)
|
||||
true
|
||||
}
|
||||
val dmCreator = action.execute(ChatActionCommand.DmCreator(creatorId = 43L)) { requirement ->
|
||||
assertEquals(AccessRequirement.Login, requirement)
|
||||
true
|
||||
}
|
||||
|
||||
assertEquals(ChatActionResult.NavigateToDmRoom(roomId = 42L), dmRoom)
|
||||
assertEquals(ChatActionResult.NavigateToCreatorDm(creatorId = 43L), dmCreator)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Owner DM command는 owner DM 목록 navigation 결과를 반환한다`() {
|
||||
val result = action.execute(ChatActionCommand.OwnerDmList) { requirement ->
|
||||
assertEquals(AccessRequirement.Login, requirement)
|
||||
true
|
||||
}
|
||||
|
||||
assertEquals(ChatActionResult.NavigateToOwnerDmList, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `로그인 접근이 차단되면 Blocked 결과를 반환한다`() {
|
||||
val result = action.execute(ChatActionCommand.AiRoom(roomId = 41L)) { requirement ->
|
||||
assertEquals(AccessRequirement.Login, requirement)
|
||||
false
|
||||
}
|
||||
|
||||
assertEquals(ChatActionResult.Blocked(AccessRequirement.Login), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handler는 AI room command를 기존 ChatRoomActivity로 연결한다`() {
|
||||
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
|
||||
|
||||
ChatActionHandler().handle(activity, ChatActionCommand.AiRoom(roomId = 44L)) { true }
|
||||
val intent = shadowOf(activity).nextStartedActivity
|
||||
|
||||
assertEquals(ChatRoomActivity::class.java.name, intent.component?.className)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handler는 DM room command에 기존 room ID extra를 전달한다`() {
|
||||
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
|
||||
|
||||
ChatActionHandler().handle(activity, ChatActionCommand.DmRoom(roomId = 45L)) { true }
|
||||
val intent = shadowOf(activity).nextStartedActivity
|
||||
|
||||
assertEquals(DmChatRoomActivity::class.java.name, intent.component?.className)
|
||||
assertEquals(45L, intent.getLongExtra(DmChatRoomActivity.EXTRA_ROOM_ID, 0L))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handler는 DM creator command에 기존 creator ID extra를 전달한다`() {
|
||||
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
|
||||
|
||||
ChatActionHandler().handle(activity, ChatActionCommand.DmCreator(creatorId = 46L)) { true }
|
||||
val intent = shadowOf(activity).nextStartedActivity
|
||||
|
||||
assertEquals(DmChatRoomActivity::class.java.name, intent.component?.className)
|
||||
assertEquals(46L, intent.getLongExtra(DmChatRoomActivity.EXTRA_CREATOR_ID, 0L))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handler는 owner DM command를 MainV2 DM 필터 intent로 연결한다`() {
|
||||
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
|
||||
|
||||
ChatActionHandler().handle(activity, ChatActionCommand.OwnerDmList) { true }
|
||||
val intent = shadowOf(activity).nextStartedActivity
|
||||
|
||||
val expected = MainV2Activity.newChatDmIntent(activity)
|
||||
|
||||
assertEquals(expected.component?.className, intent.component?.className)
|
||||
assertEquals(expected.flags, intent.flags)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handler는 invalid chat command에서 Activity를 시작하지 않는다`() {
|
||||
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
|
||||
|
||||
val result = ChatActionHandler().handle(activity, ChatActionCommand.DmRoom(roomId = 0L)) { true }
|
||||
|
||||
assertEquals(ChatActionResult.Ignored, result)
|
||||
assertNull(shadowOf(activity).nextStartedActivity)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handler는 login 차단 결과에서 Activity를 시작하지 않는다`() {
|
||||
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
|
||||
|
||||
val result = ChatActionHandler().handle(activity, ChatActionCommand.DmRoom(roomId = 45L)) { false }
|
||||
|
||||
assertEquals(ChatActionResult.Blocked(AccessRequirement.Login), result)
|
||||
assertNull(shadowOf(activity).nextStartedActivity)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `대상 화면은 채팅방 Activity 대신 Chat Action을 사용한다`() {
|
||||
val targetSources = listOf(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt",
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/ChatMainFragment.kt",
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).map { path -> projectFile(path).readText() }
|
||||
val handlerSource = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/chat/action/ChatActionHandler.kt"
|
||||
).readText()
|
||||
|
||||
targetSources.forEach { source ->
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand."))
|
||||
assertFalse(source.contains("ChatRoomActivity.newIntent"))
|
||||
assertFalse(source.contains("DmChatRoomActivity.newIntentByRoomId"))
|
||||
assertFalse(source.contains("DmChatRoomActivity.newIntentByCreatorId"))
|
||||
}
|
||||
assertTrue(handlerSource.contains("ChatRoomActivity.newIntent"))
|
||||
assertTrue(handlerSource.contains("DmChatRoomActivity.newIntentByRoomId"))
|
||||
assertTrue(handlerSource.contains("DmChatRoomActivity.newIntentByCreatorId"))
|
||||
}
|
||||
|
||||
private fun projectFile(relativePath: String): File {
|
||||
val candidates = listOf(File(relativePath), File("../$relativePath"))
|
||||
return candidates.firstOrNull { it.exists() }
|
||||
?: error("Project file not found: $relativePath")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package kr.co.vividnext.sodalive.v2.community.action
|
||||
|
||||
import android.app.Activity
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityDetailActivity
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.Shadows.shadowOf
|
||||
import org.robolectric.annotation.Config
|
||||
import java.io.File
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(application = android.app.Application::class)
|
||||
class CommunityActionTest {
|
||||
|
||||
private val action = CommunityAction()
|
||||
|
||||
@Test
|
||||
fun `유효하지 않은 post ID는 Access를 확인하지 않고 무시한다`() {
|
||||
var accessCheckCount = 0
|
||||
|
||||
val result = action.execute(CommunityActionCommand.PostDetail(postId = 0L)) {
|
||||
accessCheckCount += 1
|
||||
true
|
||||
}
|
||||
|
||||
assertEquals(CommunityActionResult.Ignored, result)
|
||||
assertEquals(0, accessCheckCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Community Post Detail은 로그인 Access가 허용되면 navigation 결과를 반환한다`() {
|
||||
var capturedRequirement: AccessRequirement? = null
|
||||
|
||||
val result = action.execute(CommunityActionCommand.PostDetail(postId = 21L)) { requirement ->
|
||||
capturedRequirement = requirement
|
||||
true
|
||||
}
|
||||
|
||||
assertEquals(AccessRequirement.Login, capturedRequirement)
|
||||
assertEquals(CommunityActionResult.NavigateToPostDetail(postId = 21L), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Access가 거부되면 Community Post Detail navigation을 차단한다`() {
|
||||
val result = action.execute(CommunityActionCommand.PostDetail(postId = 22L)) { false }
|
||||
|
||||
assertEquals(CommunityActionResult.Blocked(AccessRequirement.Login), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handler는 기본 launch로 기존 Community Detail Activity를 시작한다`() {
|
||||
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
|
||||
|
||||
val result = CommunityActionHandler().handle(
|
||||
context = activity,
|
||||
command = CommunityActionCommand.PostDetail(postId = 23L),
|
||||
ensureAccess = { true }
|
||||
)
|
||||
val intent = shadowOf(activity).nextStartedActivity
|
||||
|
||||
assertEquals(CommunityActionResult.NavigateToPostDetail(postId = 23L), result)
|
||||
assertEquals(CreatorChannelCommunityDetailActivity::class.java.name, intent.component?.className)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handler는 ActivityResultLauncher용 launch lambda를 사용할 수 있다`() {
|
||||
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
|
||||
var launchedComponent: String? = null
|
||||
|
||||
val result = CommunityActionHandler().handle(
|
||||
context = activity,
|
||||
command = CommunityActionCommand.PostDetail(postId = 24L),
|
||||
ensureAccess = { true },
|
||||
launchIntent = { intent -> launchedComponent = intent.component?.className }
|
||||
)
|
||||
|
||||
assertEquals(CommunityActionResult.NavigateToPostDetail(postId = 24L), result)
|
||||
assertEquals(CreatorChannelCommunityDetailActivity::class.java.name, launchedComponent)
|
||||
assertNull(shadowOf(activity).nextStartedActivity)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `대상 화면은 Community Detail Activity 대신 Community Action을 사용한다`() {
|
||||
val homeSource = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt"
|
||||
).readText()
|
||||
val activitySource = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val handlerSource = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/community/action/CommunityActionHandler.kt"
|
||||
).readText()
|
||||
val targetSources = listOf(homeSource, activitySource)
|
||||
|
||||
targetSources.forEach { source ->
|
||||
assertTrue(source.contains("handleCommunityAction(CommunityActionCommand.PostDetail"))
|
||||
assertFalse(source.contains("CreatorChannelCommunityDetailActivity.newIntent"))
|
||||
}
|
||||
assertTrue(handlerSource.contains("CreatorChannelCommunityDetailActivity.newIntent"))
|
||||
}
|
||||
|
||||
private fun projectFile(relativePath: String): File {
|
||||
val candidates = listOf(File(relativePath), File("../$relativePath"))
|
||||
return candidates.firstOrNull { it.exists() }
|
||||
?: error("Project file not found: $relativePath")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package kr.co.vividnext.sodalive.v2.community.action
|
||||
|
||||
import android.app.Activity
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class CommunityChangeTest {
|
||||
|
||||
@Test
|
||||
fun `작성 Activity RESULT_OK는 Created 변경으로 변환한다`() {
|
||||
val change = resolveCommunityActivityResult(
|
||||
source = CommunityActivityResultSource.Write,
|
||||
resultCode = Activity.RESULT_OK
|
||||
)
|
||||
|
||||
assertEquals(CommunityChange.Created, change)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `수정 Activity RESULT_OK는 Updated 변경으로 변환한다`() {
|
||||
val change = resolveCommunityActivityResult(
|
||||
source = CommunityActivityResultSource.Modify,
|
||||
resultCode = Activity.RESULT_OK
|
||||
)
|
||||
|
||||
assertEquals(CommunityChange.Updated, change)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `상세 Activity RESULT_OK는 Updated 변경으로 변환한다`() {
|
||||
val change = resolveCommunityActivityResult(
|
||||
source = CommunityActivityResultSource.Detail,
|
||||
resultCode = Activity.RESULT_OK
|
||||
)
|
||||
|
||||
assertEquals(CommunityChange.Updated, change)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `취소 또는 실패 결과는 Community 변경을 무시한다`() {
|
||||
val changes = CommunityActivityResultSource.entries.map { source ->
|
||||
resolveCommunityActivityResult(source = source, resultCode = Activity.RESULT_CANCELED)
|
||||
}
|
||||
|
||||
assertEquals(List(CommunityActivityResultSource.entries.size) { CommunityChange.Ignored }, changes)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `삭제와 고정 변경은 post ID를 가진 명시적 변경으로 표현한다`() {
|
||||
val deleted = CommunityChange.Deleted(postId = 31L)
|
||||
val pinChanged = CommunityChange.PinChanged(postId = 32L)
|
||||
|
||||
assertEquals(31L, deleted.postId)
|
||||
assertEquals(32L, pinChanged.postId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.action
|
||||
|
||||
import android.app.Activity
|
||||
import kr.co.vividnext.sodalive.v2.access.AccessRequirement
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.Shadows.shadowOf
|
||||
import org.robolectric.annotation.Config
|
||||
import java.io.File
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(application = android.app.Application::class)
|
||||
class CreatorActionTest {
|
||||
|
||||
private val action = CreatorAction()
|
||||
|
||||
@Test
|
||||
fun `유효하지 않은 creator ID는 Access를 확인하지 않고 무시한다`() {
|
||||
var accessCheckCount = 0
|
||||
|
||||
val result = action.execute(CreatorActionCommand.Profile(creatorId = 0L)) {
|
||||
accessCheckCount += 1
|
||||
true
|
||||
}
|
||||
|
||||
assertEquals(CreatorActionResult.Ignored, result)
|
||||
assertEquals(0, accessCheckCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Creator Profile은 로그인 Access가 허용되면 navigation 결과를 반환한다`() {
|
||||
var capturedRequirement: AccessRequirement? = null
|
||||
|
||||
val result = action.execute(CreatorActionCommand.Profile(creatorId = 11L)) { requirement ->
|
||||
capturedRequirement = requirement
|
||||
true
|
||||
}
|
||||
|
||||
assertEquals(AccessRequirement.Login, capturedRequirement)
|
||||
assertEquals(CreatorActionResult.NavigateToCreatorProfile(creatorId = 11L), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Access가 거부되면 Creator Profile navigation을 차단한다`() {
|
||||
val result = action.execute(CreatorActionCommand.Profile(creatorId = 12L)) { false }
|
||||
|
||||
assertEquals(CreatorActionResult.Blocked(AccessRequirement.Login), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handler는 허용된 Creator Profile에 기존 creator ID extra를 전달한다`() {
|
||||
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
|
||||
|
||||
val result = CreatorActionHandler().handle(
|
||||
context = activity,
|
||||
command = CreatorActionCommand.Profile(creatorId = 13L),
|
||||
ensureAccess = { true }
|
||||
)
|
||||
val intent = shadowOf(activity).nextStartedActivity
|
||||
|
||||
assertEquals(CreatorActionResult.NavigateToCreatorProfile(creatorId = 13L), result)
|
||||
assertEquals(CreatorChannelActivity::class.java.name, intent.component?.className)
|
||||
assertEquals(13L, intent.getLongExtra(CreatorChannelActivity.EXTRA_CREATOR_ID, 0L))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handler는 invalid Creator Profile에서 Activity를 시작하지 않는다`() {
|
||||
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
|
||||
|
||||
val result = CreatorActionHandler().handle(
|
||||
context = activity,
|
||||
command = CreatorActionCommand.Profile(creatorId = -1L),
|
||||
ensureAccess = { true }
|
||||
)
|
||||
|
||||
assertEquals(CreatorActionResult.Ignored, result)
|
||||
assertNull(shadowOf(activity).nextStartedActivity)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `대상 화면은 Creator Channel Activity 대신 Creator Action을 사용한다`() {
|
||||
val homeSource = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt"
|
||||
).readText()
|
||||
val homeModelSource = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeRecommendationUiModels.kt"
|
||||
).readText()
|
||||
val contentModelSource = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/model/AudioRecommendationsUiModels.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(homeSource.contains("handleCreatorAction(CreatorActionCommand.Profile(creatorId))"))
|
||||
assertFalse(homeModelSource.contains("CreatorChannelActivity.newIntent"))
|
||||
assertFalse(contentModelSource.contains("CreatorChannelActivity.newIntent"))
|
||||
}
|
||||
|
||||
private fun projectFile(relativePath: String): File {
|
||||
val candidates = listOf(File(relativePath), File("../$relativePath"))
|
||||
return candidates.firstOrNull { it.exists() }
|
||||
?: error("Project file not found: $relativePath")
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,8 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(source.contains("if (creatorId <= 0L)"))
|
||||
assertTrue(source.contains("finish()"))
|
||||
assertFalse(source.contains("is CreatorChannelHomeUiState.Error -> showToast"))
|
||||
assertTrue(source.contains("ChatRoomActivity.newIntent(this, chatRoomId)"))
|
||||
assertTrue(source.contains("DmChatRoomActivity.newIntentByCreatorId(this, creatorId)"))
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.AiRoom(chatRoomId))"))
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.DmCreator(creatorId))"))
|
||||
assertTrue(source.contains("homeActionDelegate?.createChatRoom(characterId)"))
|
||||
assertTrue(source.contains("updateActionButtonLayout"))
|
||||
assertTrue(source.contains("marginStart = if (isChatVisible && isDmVisible)"))
|
||||
@@ -93,7 +93,7 @@ class CreatorChannelActivitySourceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `커뮤니티 게시물 클릭 source는 상세 Activity newIntent로 진입하고 invalid postId를 무시한다`() {
|
||||
fun `커뮤니티 게시물 클릭 source는 공통 Community Action으로 진입한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
@@ -104,27 +104,19 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(handlerEnd > handlerStart)
|
||||
val handlerSource = source.substring(handlerStart, handlerEnd)
|
||||
|
||||
assertTrue(
|
||||
assertFalse(
|
||||
source.contains(
|
||||
"import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityDetailActivity"
|
||||
)
|
||||
)
|
||||
assertTrue(handlerSource.contains("if (postId <= 0L) return"))
|
||||
assertTrue(handlerSource.contains("ensureV2Access(AccessRequirement.Login)"))
|
||||
assertTrue(handlerSource.contains("CreatorChannelCommunityDetailActivity.newIntent(this, postId)"))
|
||||
assertTrue(handlerSource.contains("handleCommunityAction(CommunityActionCommand.PostDetail(postId)"))
|
||||
assertTrue(source.contains("private val communityDetailLauncher = registerForActivityResult"))
|
||||
assertTrue(
|
||||
handlerSource.contains(
|
||||
"communityDetailLauncher.launch(CreatorChannelCommunityDetailActivity.newIntent(this, postId))"
|
||||
"handleCommunityAction(CommunityActionCommand.PostDetail(postId), communityDetailLauncher::launch)"
|
||||
)
|
||||
)
|
||||
assertTrue(
|
||||
handlerSource.indexOf("ensureV2Access(AccessRequirement.Login)") <
|
||||
handlerSource.indexOf(
|
||||
"communityDetailLauncher.launch(CreatorChannelCommunityDetailActivity.newIntent(this, postId))"
|
||||
)
|
||||
)
|
||||
assertTrue(source.contains("refreshCreatorChannelCommunity()"))
|
||||
assertTrue(source.contains("private fun handleCommunityChange(change: CommunityChange)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -229,7 +221,7 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(layout.contains("android:id=\"@+id/tv_chat_button\""))
|
||||
assertTrue(layout.contains("android:id=\"@+id/tv_dm_button\""))
|
||||
assertTrue(layout.contains("tools:visibility=\"visible\""))
|
||||
assertTrue(source.contains("MainV2Activity.newChatDmIntent(this)"))
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.OwnerDmList)"))
|
||||
assertTrue(
|
||||
source.contains(
|
||||
"if (header.isOwner) R.string.creator_channel_dm_check_button else R.string.creator_channel_dm_button"
|
||||
@@ -245,7 +237,7 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(chat.contains("selectedIndex = initialFilter.tabIndex"))
|
||||
assertTrue(chat.contains("fun selectFilter(filter: ChatRoomFilter)"))
|
||||
assertTrue(filter.contains("DM(\"DM\", 2)"))
|
||||
assertTrue(source.contains("DmChatRoomActivity.newIntentByCreatorId(this, creatorId)"))
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.DmCreator(creatorId))"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -665,6 +657,86 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(fragment.contains("mediaPlayerManager?.stopContent()"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `커뮤니티 고정 성공은 실제 post id로 단일 변경 handler를 호출한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val fixedSource = sourceSection(
|
||||
source = source,
|
||||
startMarker = "private fun updateCreatorChannelCommunityPostFixed",
|
||||
endMarker = "private fun deleteCreatorChannelCommunityPost"
|
||||
)
|
||||
val successMarker = "if (response.success)"
|
||||
val beforeSuccessSource = fixedSource.substring(0, fixedSource.indexOf(successMarker))
|
||||
val successSource = fixedSource.substring(
|
||||
fixedSource.indexOf(successMarker),
|
||||
fixedSource.indexOf("} else", fixedSource.indexOf(successMarker))
|
||||
)
|
||||
|
||||
assertTrue(fixedSource.contains("postId = item.postId"))
|
||||
assertTrue(fixedSource.contains("isFixed = !item.isPinned"))
|
||||
assertTrue(successSource.contains("handleCommunityChange(CommunityChange.PinChanged(item.postId))"))
|
||||
assertFalse(beforeSuccessSource.contains("CommunityChange.PinChanged"))
|
||||
assertEquals(1, fixedSource.split("CommunityChange.PinChanged").size - 1)
|
||||
assertFalse(fixedSource.contains("CommunityChange.PinChanged(creatorId)"))
|
||||
assertFalse(fixedSource.contains("refreshCreatorChannelCommunity()"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `커뮤니티 삭제 성공은 실제 post id로 단일 변경 handler를 호출한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val deleteSource = sourceSection(
|
||||
source = source,
|
||||
startMarker = "private fun deleteCreatorChannelCommunityPost",
|
||||
endMarker = "private fun authToken()"
|
||||
)
|
||||
val successMarker = "if (response.success)"
|
||||
val beforeSuccessSource = deleteSource.substring(0, deleteSource.indexOf(successMarker))
|
||||
val successSource = deleteSource.substring(
|
||||
deleteSource.indexOf(successMarker),
|
||||
deleteSource.indexOf("} else", deleteSource.indexOf(successMarker))
|
||||
)
|
||||
|
||||
assertTrue(deleteSource.contains("creatorCommunityId = item.postId"))
|
||||
assertTrue(deleteSource.contains("isActive = false"))
|
||||
assertTrue(successSource.contains("handleCommunityChange(CommunityChange.Deleted(item.postId))"))
|
||||
assertFalse(beforeSuccessSource.contains("CommunityChange.Deleted"))
|
||||
assertEquals(1, deleteSource.split("CommunityChange.Deleted").size - 1)
|
||||
assertFalse(deleteSource.contains("CommunityChange.Deleted(creatorId)"))
|
||||
assertFalse(deleteSource.contains("refreshCreatorChannelCommunity()"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `커뮤니티 변경 handler는 변경 종류별 projection 갱신을 단일 소유한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val handlerSource = sourceSection(
|
||||
source = source,
|
||||
startMarker = "private fun handleCommunityChange(change: CommunityChange)",
|
||||
endMarker = "private fun refreshCreatorChannelFanTalk()"
|
||||
)
|
||||
val createdSource = handlerSource.substring(
|
||||
handlerSource.indexOf("CommunityChange.Created"),
|
||||
handlerSource.indexOf("CommunityChange.Updated")
|
||||
)
|
||||
val mutationSource = handlerSource.substring(handlerSource.indexOf("CommunityChange.Updated"))
|
||||
|
||||
assertTrue(createdSource.contains("homeActionDelegate?.refreshHome()"))
|
||||
assertTrue(createdSource.contains("refreshCreatorChannelCommunity()"))
|
||||
assertTrue(
|
||||
mutationSource.contains(
|
||||
"CommunityChange.Updated,\n" +
|
||||
" is CommunityChange.Deleted,\n" +
|
||||
" is CommunityChange.PinChanged -> refreshCreatorChannelCommunity()"
|
||||
)
|
||||
)
|
||||
assertFalse(mutationSource.contains("homeActionDelegate?.refreshHome()"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `FanTalk tab source는 Fragment Host pagination height delete dialog를 Activity에 연결한다`() {
|
||||
val source = projectFile(
|
||||
@@ -1100,7 +1172,7 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(fragment.contains("onCommunityClick = ::onCommunityClicked"))
|
||||
assertTrue(fragment.contains("host.onCreatorChannelCommunityPostClicked(postId)"))
|
||||
assertTrue(activity.contains("override fun onCreatorChannelCommunityPostClicked(postId: Long)"))
|
||||
assertTrue(activity.contains("CreatorChannelCommunityDetailActivity.newIntent(this, postId)"))
|
||||
assertTrue(activity.contains("handleCommunityAction(CommunityActionCommand.PostDetail(postId)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -8,15 +8,17 @@ import java.io.File
|
||||
class MainV2ActivitySourceTest {
|
||||
|
||||
@Test
|
||||
fun `MainV2Activity는 chat_type 없이 chat path만 DM 채팅방으로 라우팅한다`() {
|
||||
fun `MainV2Activity는 chat path DM을 Chat Action으로 라우팅한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt").readText()
|
||||
|
||||
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity"))
|
||||
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.chat.action.ChatActionCommand"))
|
||||
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.chat.action.handleChatAction"))
|
||||
assertFalse(source.contains("chat_type"))
|
||||
assertFalse(source.contains("isUserCreatorChat"))
|
||||
assertTrue(source.contains("return bundle.getString(\"deep_link_value\") == \"chat\""))
|
||||
assertTrue(source.contains("val roomId = bundle.getString(\"room_id\")?.toLongOrNull()"))
|
||||
assertTrue(source.contains("DmChatRoomActivity.newIntentByRoomId(applicationContext, roomId)"))
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.DmRoom(roomId))"))
|
||||
assertFalse(source.contains("DmChatRoomActivity.newIntentByRoomId(applicationContext, roomId)"))
|
||||
assertTrue(source.contains("if (isDmChatDeepLink(bundle) && roomId != null && roomId > 0)"))
|
||||
assertFalse(source.contains("private fun isLoggedIn()"))
|
||||
assertTrue(source.contains("fun showLoginActivity()"))
|
||||
@@ -36,6 +38,15 @@ class MainV2ActivitySourceTest {
|
||||
assertTrue(source.contains("putQuery(\"room_id\")"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MainV2Activity는 community postId query를 canonical extra로 정규화한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt").readText()
|
||||
|
||||
assertTrue(source.contains("putQuery(\"postId\")"))
|
||||
assertTrue(source.contains("extras.getString(\"postId\")?.takeIf { it.isNotBlank() }?.let"))
|
||||
assertTrue(source.contains("extras.putString(Constants.EXTRA_COMMUNITY_POST_ID, it)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MainV2Activity는 chat path deep_link 단독 payload를 DM 채팅방으로 라우팅한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt").readText()
|
||||
@@ -53,6 +64,104 @@ class MainV2ActivitySourceTest {
|
||||
assertTrue(dmRouteIndex < firstFallbackIndex)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MainV2Activity는 non-DM room을 channel보다 먼저 Live Action으로 라우팅한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt").readText()
|
||||
val routeSource = source.substringFrom("private fun executeBundleRoute(bundle: Bundle): Boolean")
|
||||
|
||||
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinator"))
|
||||
assertTrue(source.contains("private val liveActionCoordinator: LiveActionCoordinator by lazy"))
|
||||
assertTrue(routeSource.contains("roomId != null && roomId > 0 ->"))
|
||||
assertTrue(routeSource.contains("liveActionCoordinator.enterLiveRoom(roomId)"))
|
||||
assertBefore(
|
||||
routeSource,
|
||||
"roomId != null && roomId > 0 ->",
|
||||
"channelId != null && channelId > 0 ->"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MainV2Activity는 cold start 딥링크 공통 대기와 Live 조회에 문구 없는 로딩을 유지한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt").readText()
|
||||
val onCreateSource = source.substringFrom("override fun onCreate(savedInstanceState: Bundle?)")
|
||||
val observerSource = source.substringFrom("private fun setupLiveEntryObservers()")
|
||||
val updateSource = source.substringFrom("private fun updateRouteLoadingDialog()")
|
||||
val setupViewSource = source.substringFrom("override fun setupView()")
|
||||
|
||||
assertTrue(source.contains("import kr.co.vividnext.sodalive.common.LoadingDialog"))
|
||||
assertTrue(source.contains("private lateinit var routeLoadingDialog: LoadingDialog"))
|
||||
assertTrue(source.contains("private var isDeferredRouteLoading = false"))
|
||||
assertTrue(source.contains("private var isLiveEntryLoading = false"))
|
||||
assertTrue(observerSource.contains("routeLoadingDialog = LoadingDialog(this, layoutInflater)"))
|
||||
assertTrue(observerSource.contains("liveViewModel.toastLiveData.observe(this)"))
|
||||
assertTrue(observerSource.contains("it?.let(::showToast)"))
|
||||
assertTrue(observerSource.contains("liveViewModel.isLoading.observe(this)"))
|
||||
assertTrue(observerSource.contains("isLiveEntryLoading = isLoading"))
|
||||
assertTrue(observerSource.contains("updateRouteLoadingDialog()"))
|
||||
assertTrue(
|
||||
onCreateSource.contains(
|
||||
"intent.hasExtra(Constants.EXTRA_DATA) || intent.hasExtra(EXTRA_AUDIO_NOTIFICATION_ROUTE)"
|
||||
)
|
||||
)
|
||||
assertBefore(onCreateSource, "isDeferredRouteLoading =", "handler.postDelayed")
|
||||
assertTrue(onCreateSource.contains("isDeferredRouteLoading = false"))
|
||||
assertTrue(updateSource.contains("if (isDeferredRouteLoading || isLiveEntryLoading)"))
|
||||
assertTrue(updateSource.contains("routeLoadingDialog.show(screenWidth)"))
|
||||
assertFalse(updateSource.contains("R.string.screen_live_loading"))
|
||||
assertTrue(updateSource.contains("routeLoadingDialog.dismiss()"))
|
||||
assertTrue(setupViewSource.contains("setupLiveEntryObservers()"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MainV2Activity는 Community post를 creator fallback보다 먼저 Action으로 라우팅한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt").readText()
|
||||
val routeSource = source.substringFrom("private fun executeBundleRoute(bundle: Bundle): Boolean")
|
||||
|
||||
assertTrue(routeSource.contains("communityPostId != null && communityPostId > 0 ->"))
|
||||
assertTrue(
|
||||
routeSource.contains(
|
||||
"handleCommunityAction(CommunityActionCommand.PostDetail(communityPostId))"
|
||||
)
|
||||
)
|
||||
assertTrue(routeSource.contains("communityCreatorId != null && communityCreatorId > 0 ->"))
|
||||
assertTrue(
|
||||
routeSource.contains(
|
||||
"handleCreatorAction(CreatorActionCommand.Profile(communityCreatorId))"
|
||||
)
|
||||
)
|
||||
assertBefore(
|
||||
routeSource,
|
||||
"communityPostId != null && communityPostId > 0 ->",
|
||||
"communityCreatorId != null && communityCreatorId > 0 ->"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MainV2Activity는 deep-link 도메인 상세를 기존 Action으로 실행한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt").readText()
|
||||
val routeSource = source.substringFrom("private fun routeByDeepLinkValue(")
|
||||
val communityRoute = routeSource.substringAfter("\"community\" ->").substringBefore("\"message\" ->")
|
||||
val messageRoute = routeSource.substringAfter("\"message\" ->").substringBefore("\"audition\" ->")
|
||||
|
||||
assertTrue(routeSource.contains("handleContentAction(ContentActionCommand.SeriesDetail(deepLinkValueId))"))
|
||||
assertTrue(routeSource.contains("handleContentAction(ContentActionCommand.AudioDetail(deepLinkValueId))"))
|
||||
assertTrue(routeSource.contains("handleCreatorAction(CreatorActionCommand.Profile(deepLinkValueId))"))
|
||||
assertTrue(routeSource.contains("liveActionCoordinator.enterLiveRoom(deepLinkValueId)"))
|
||||
assertTrue(communityRoute.contains("handleCreatorAction(CreatorActionCommand.Profile(deepLinkValueId))"))
|
||||
assertFalse(communityRoute.contains("CommunityActionCommand.PostDetail"))
|
||||
assertTrue(messageRoute.contains("MessageActivity::class.java"))
|
||||
assertFalse(messageRoute.contains("ChatActionCommand.DmRoom"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MainV2Activity는 audio detail notification을 Content Action으로 실행한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt").readText()
|
||||
val routeSource = source.substringFrom("private fun handleAudioNotificationRoute(intent: Intent): Boolean")
|
||||
|
||||
assertTrue(routeSource.contains("handleContentAction(ContentActionCommand.AudioDetail(contentId))"))
|
||||
assertFalse(routeSource.contains("Intent(applicationContext, AudioContentDetailActivity::class.java)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MainV2Activity 대화 탭 이동은 로그인 가드를 통과한 뒤 탭을 전환한다`() {
|
||||
val source = projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt").readText()
|
||||
|
||||
@@ -17,7 +17,7 @@ class MainV2AudioNotificationRouteSourceTest {
|
||||
assertTrue(source.contains("const val ROUTE_AUDIO_DETAIL"))
|
||||
assertTrue(source.contains("handleAudioNotificationRoute"))
|
||||
assertTrue(source.contains("showPlayerFragment()"))
|
||||
assertTrue(source.contains("AudioContentDetailActivity::class.java"))
|
||||
assertTrue(source.contains("ContentActionCommand.AudioDetail"))
|
||||
assertTrue(source.contains("Constants.EXTRA_AUDIO_CONTENT_ID"))
|
||||
assertTrue(source.contains("removeExtra(EXTRA_AUDIO_NOTIFICATION_ROUTE)"))
|
||||
}
|
||||
@@ -41,7 +41,11 @@ class MainV2AudioNotificationRouteSourceTest {
|
||||
assertTrue(routeSource.contains("val contentId = intent.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)"))
|
||||
assertTrue(routeSource.contains("intent.removeExtra(Constants.EXTRA_AUDIO_CONTENT_ID)"))
|
||||
assertTrue(routeSource.contains("if (contentId > 0"))
|
||||
assertBefore(routeSource, "if (contentId > 0", "AudioContentDetailActivity::class.java")
|
||||
assertBefore(
|
||||
routeSource,
|
||||
"if (contentId > 0",
|
||||
"handleContentAction(ContentActionCommand.AudioDetail(contentId))"
|
||||
)
|
||||
}
|
||||
|
||||
private fun assertBefore(source: String, expectedBefore: String, expectedAfter: String) {
|
||||
|
||||
@@ -132,9 +132,11 @@ class ChatMainFragmentLayoutTest {
|
||||
assertTrue(source.contains("private fun onChatRoomClick(item: ChatRoomListUiItem)"))
|
||||
assertTrue(source.contains("when (item.chatType)"))
|
||||
assertTrue(source.contains("ChatRoomType.AI ->"))
|
||||
assertTrue(source.contains("ChatRoomActivity.newIntent(requireContext(), item.roomId)"))
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.AiRoom(item.roomId))"))
|
||||
assertTrue(source.contains("ChatRoomType.DM ->"))
|
||||
assertTrue(source.contains("DmChatRoomActivity.newIntentByRoomId(requireContext(), item.roomId)"))
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.DmRoom(item.roomId))"))
|
||||
assertFalse(source.contains("ChatRoomActivity.newIntent(requireContext(), item.roomId)"))
|
||||
assertFalse(source.contains("DmChatRoomActivity.newIntentByRoomId(requireContext(), item.roomId)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -6,7 +6,6 @@ import kr.co.vividnext.sodalive.BuildConfig
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
|
||||
import kr.co.vividnext.sodalive.settings.event.EventItem
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
import kr.co.vividnext.sodalive.v2.main.content.model.ContentBannerRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.content.model.ContentBannerUiModel
|
||||
import kr.co.vividnext.sodalive.v2.main.content.model.toContentBannerIntent
|
||||
@@ -467,7 +466,7 @@ class ContentMainFragmentSourceTest {
|
||||
val context = RuntimeEnvironment.getApplication() as Context
|
||||
val eventItem = EventItem(id = 1L, thumbnailImageUrl = "https://example.com/event.png")
|
||||
val eventIntent = requireNotNull(ContentBannerRoute.Event(eventItem).toContentBannerIntent(context))
|
||||
val creatorIntent = requireNotNull(ContentBannerRoute.Creator(2L).toContentBannerIntent(context))
|
||||
val creatorIntent = ContentBannerRoute.Creator(2L).toContentBannerIntent(context)
|
||||
val seriesIntent = ContentBannerRoute.Series(3L).toContentBannerIntent(context)
|
||||
val webIntent = requireNotNull(
|
||||
ContentBannerRoute.Link("https://example.com", isWebUrl = true).toContentBannerIntent(context)
|
||||
@@ -481,8 +480,7 @@ class ContentMainFragmentSourceTest {
|
||||
|
||||
assertEquals(EventDetailActivity::class.java.name, eventIntent.component?.className)
|
||||
assertEquals(eventItem, eventIntent.getParcelableExtra(Constants.EXTRA_EVENT))
|
||||
assertEquals(CreatorChannelActivity::class.java.name, creatorIntent.component?.className)
|
||||
assertEquals(2L, creatorIntent.getLongExtra(CreatorChannelActivity.EXTRA_CREATOR_ID, 0L))
|
||||
assertNull(creatorIntent)
|
||||
assertNull(seriesIntent)
|
||||
assertEquals(android.content.Intent.ACTION_VIEW, webIntent.action)
|
||||
assertEquals("https://example.com", webIntent.data.toString())
|
||||
|
||||
@@ -27,7 +27,6 @@ import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||
import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
|
||||
import kr.co.vividnext.sodalive.settings.event.EventItem
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
import kr.co.vividnext.sodalive.v2.main.home.data.HomeActiveCreatorItem
|
||||
import kr.co.vividnext.sodalive.v2.main.home.data.HomeAiCharacterItem
|
||||
import kr.co.vividnext.sodalive.v2.main.home.data.HomeBannerItem
|
||||
@@ -56,7 +55,6 @@ import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationRecentlyAct
|
||||
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationBannerIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationBannerRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationAiCharacterIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationAiCharacterRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationRecentlyActiveCreatorRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.visibleHomePopularCommunityPosts
|
||||
@@ -872,9 +870,7 @@ class HomeMainFragmentLayoutTest {
|
||||
val eventIntent = requireNotNull(
|
||||
HomeRecommendationBannerRoute.Event(eventItem).toHomeRecommendationBannerIntent(context)
|
||||
)
|
||||
val creatorIntent = requireNotNull(
|
||||
HomeRecommendationBannerRoute.Creator(2L).toHomeRecommendationBannerIntent(context)
|
||||
)
|
||||
val creatorIntent = HomeRecommendationBannerRoute.Creator(2L).toHomeRecommendationBannerIntent(context)
|
||||
val seriesIntent = HomeRecommendationBannerRoute.Series(3L).toHomeRecommendationBannerIntent(context)
|
||||
val webIntent = requireNotNull(
|
||||
HomeRecommendationBannerRoute.Link(
|
||||
@@ -891,8 +887,7 @@ class HomeMainFragmentLayoutTest {
|
||||
|
||||
assertEquals(EventDetailActivity::class.java.name, eventIntent.component?.className)
|
||||
assertEquals(eventItem, eventIntent.getParcelableExtra(Constants.EXTRA_EVENT))
|
||||
assertEquals(CreatorChannelActivity::class.java.name, creatorIntent.component?.className)
|
||||
assertEquals(2L, creatorIntent.getLongExtra(CreatorChannelActivity.EXTRA_CREATOR_ID, 0L))
|
||||
assertNull(creatorIntent)
|
||||
assertNull(seriesIntent)
|
||||
assertEquals(android.content.Intent.ACTION_VIEW, webIntent.action)
|
||||
assertEquals("https://example.com", webIntent.data.toString())
|
||||
@@ -992,15 +987,6 @@ class HomeMainFragmentLayoutTest {
|
||||
assertEquals(null, aiCharacter(creatorId = -1L).toHomeRecommendationAiCharacterRoute())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `home ai character route creates creator channel intent`() {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
val intent = HomeRecommendationAiCharacterRoute.Creator(22L).toHomeRecommendationAiCharacterIntent(context)
|
||||
|
||||
assertEquals(CreatorChannelActivity::class.java.name, intent.component?.className)
|
||||
assertEquals(22L, intent.getLongExtra(CreatorChannelActivity.EXTRA_CREATOR_ID, 0L))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `home popular community adapter applies blur when locked paid post image is loaded`() {
|
||||
val source = projectFile(
|
||||
|
||||
@@ -15,15 +15,11 @@ class HomeMainFragmentLoginGuardSourceTest {
|
||||
|
||||
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.access.ensureV2Access"))
|
||||
assertGuardedStartActivity(source, "private fun openHomeOnAirLive()")
|
||||
assertGuardedStartActivity(source, "private fun openFollowingChat(item: ChatRoomListUiItem)")
|
||||
assertGuardedStartActivity(source, "private fun onBannerClick(item: HomeRecommendationBannerUiModel)")
|
||||
assertGuardedStartActivity(
|
||||
source,
|
||||
"private fun onRecentActivityClick(item: HomeRecommendationRecentlyActiveCreatorUiModel)"
|
||||
)
|
||||
assertGuardedStartActivity(source, "private fun onAiCharacterClick(item: HomeRecommendationAiCharacterUiModel)")
|
||||
assertGuardedStartActivity(source, "private fun openCreatorProfile(creatorId: Long)")
|
||||
assertGuardedStartActivity(source, "private fun openPopularCommunityPost(item: FeedItem.Community)")
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.AiRoom"))
|
||||
assertTrue(source.contains("handleChatAction(ChatActionCommand.DmRoom"))
|
||||
assertTrue(source.contains("handleCommunityAction(CommunityActionCommand.PostDetail"))
|
||||
assertTrue(source.contains("handleCreatorAction(CreatorActionCommand.Profile"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -33,9 +29,9 @@ class HomeMainFragmentLoginGuardSourceTest {
|
||||
).readText()
|
||||
|
||||
assertBeforeGuard(source, "val route = item.toHomeRecommendationBannerRoute() ?: return")
|
||||
assertBeforeGuard(source, "val route = item.toHomeRecommendationRecentlyActiveCreatorRoute() ?: return")
|
||||
assertBeforeGuard(source, "val route = item.toHomeRecommendationAiCharacterRoute() ?: return")
|
||||
assertBeforeGuard(source, "val postId = item.postId.toLongOrNull() ?: return")
|
||||
assertBefore(source, "val route = item.toHomeRecommendationRecentlyActiveCreatorRoute() ?: return", "handle")
|
||||
assertBefore(source, "val route = item.toHomeRecommendationAiCharacterRoute() ?: return", "handle")
|
||||
assertBefore(source, "val postId = item.postId.toLongOrNull() ?: return", "handleCommunityAction")
|
||||
assertFalse(source.contains("requiresAdultContentAccess = true"))
|
||||
}
|
||||
|
||||
@@ -46,7 +42,8 @@ class HomeMainFragmentLoginGuardSourceTest {
|
||||
).readText()
|
||||
|
||||
val clickSource = source.substringFrom("private fun openPopularCommunityPost(item: FeedItem.Community)")
|
||||
assertTrue(clickSource.contains("CreatorChannelCommunityDetailActivity.newIntent(requireContext(), postId)"))
|
||||
assertTrue(clickSource.contains("handleCommunityAction(CommunityActionCommand.PostDetail(postId))"))
|
||||
assertFalse(clickSource.contains("CreatorChannelCommunityDetailActivity.newIntent"))
|
||||
assertFalse(clickSource.contains("CreatorCommunityAllActivity"))
|
||||
assertFalse(clickSource.contains("EXTRA_COMMUNITY_CREATOR_ID"))
|
||||
assertFalse(clickSource.contains("EXTRA_COMMUNITY_POST_ID"))
|
||||
@@ -65,14 +62,15 @@ class HomeMainFragmentLoginGuardSourceTest {
|
||||
assertBefore(
|
||||
clickSource,
|
||||
"val community = item as? HomeFollowingNewsUiItem.Community ?: return",
|
||||
"ensureV2Access"
|
||||
"handleCommunityAction"
|
||||
)
|
||||
assertBefore(
|
||||
clickSource,
|
||||
"if (postId <= 0L) return",
|
||||
"ensureV2Access"
|
||||
"handleCommunityAction"
|
||||
)
|
||||
assertTrue(clickSource.contains("CreatorChannelCommunityDetailActivity.newIntent(requireContext(), postId)"))
|
||||
assertTrue(clickSource.contains("handleCommunityAction(CommunityActionCommand.PostDetail(postId))"))
|
||||
assertFalse(clickSource.contains("CreatorChannelCommunityDetailActivity.newIntent"))
|
||||
assertFalse(clickSource.contains("CreatorCommunityAllActivity"))
|
||||
assertFalse(clickSource.contains("EXTRA_COMMUNITY_POST_ID"))
|
||||
}
|
||||
@@ -177,6 +175,14 @@ class HomeMainFragmentLoginGuardSourceTest {
|
||||
)
|
||||
}
|
||||
|
||||
private fun assertGuardedAction(source: String, functionSignature: String, actionCall: String) {
|
||||
val functionSource = source.substringFrom(functionSignature)
|
||||
assertTrue(
|
||||
"$functionSignature must call ensureV2Access before $actionCall.",
|
||||
functionSource.indexOf("ensureV2Access") in 0 until functionSource.indexOf(actionCall)
|
||||
)
|
||||
}
|
||||
|
||||
private fun assertBeforeGuard(source: String, expectedReturn: String) {
|
||||
val returnIndex = source.indexOf(expectedReturn)
|
||||
val guardIndex = source.indexOf("ensureV2Access", returnIndex)
|
||||
|
||||
Reference in New Issue
Block a user