fix(creator): 채널 라이브 진입을 보강한다
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package kr.co.vividnext.sodalive.explorer.profile.creator_community.write
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -26,9 +27,11 @@ import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import org.koin.android.ext.android.inject
|
||||
import java.io.File
|
||||
|
||||
class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWriteBinding>(
|
||||
class CreatorCommunityWriteActivity :
|
||||
BaseActivity<ActivityCreatorCommunityWriteBinding>(
|
||||
ActivityCreatorCommunityWriteBinding::inflate
|
||||
), RecordingVoiceFragment.OnAudioRecordedListener {
|
||||
),
|
||||
RecordingVoiceFragment.OnAudioRecordedListener {
|
||||
|
||||
private val viewModel: CreatorCommunityWriteViewModel by inject()
|
||||
|
||||
@@ -62,7 +65,8 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
|
||||
context = this,
|
||||
isEnabledFreeStyleCrop = true,
|
||||
config = ImagePickerCropper.Config(
|
||||
aspectX = 1f, aspectY = 1f,
|
||||
aspectX = 1f,
|
||||
aspectY = 1f,
|
||||
compressFormat = Bitmap.CompressFormat.JPEG,
|
||||
compressQuality = 90
|
||||
),
|
||||
@@ -112,7 +116,10 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
|
||||
binding.llPriceFree.setOnClickListener { viewModel.setPriceFree(true) }
|
||||
binding.tvCancel.setOnClickListener { finish() }
|
||||
binding.tvUpload.setOnClickListener {
|
||||
viewModel.createCommunityPost { finish() }
|
||||
viewModel.createCommunityPost {
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,13 @@ import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.view.Gravity
|
||||
import android.view.animation.Interpolator
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.MeasureSpec
|
||||
import android.widget.LinearLayout
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
@@ -22,6 +24,7 @@ import androidx.core.view.updatePadding
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.google.gson.Gson
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
|
||||
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
|
||||
@@ -29,18 +32,27 @@ import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateActivity
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.write.CreatorCommunityWriteActivity
|
||||
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.base.SodaDialog
|
||||
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailFragment
|
||||
import kr.co.vividnext.sodalive.live.LiveViewModel
|
||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationDialog
|
||||
import kr.co.vividnext.sodalive.mypage.MyPageViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.auth.Auth
|
||||
import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
|
||||
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
|
||||
import kr.co.vividnext.sodalive.report.ProfileReportDialog
|
||||
import kr.co.vividnext.sodalive.report.UserReportDialog
|
||||
import kr.co.vividnext.sodalive.settings.ContentSettingsActivity
|
||||
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelAudioContentResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelLiveResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelScheduleResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelSeriesResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelHeaderUiModel
|
||||
@@ -49,11 +61,16 @@ import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelTab
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelTitleBarState
|
||||
import kr.co.vividnext.sodalive.v2.main.MainV2Activity
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.splash.SplashActivity
|
||||
import kr.co.vividnext.sodalive.user.login.LoginActivity
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class CreatorChannelActivity :
|
||||
BaseActivity<ActivityCreatorChannelBinding>(ActivityCreatorChannelBinding::inflate),
|
||||
CreatorChannelHomeFragment.Host {
|
||||
|
||||
private val liveViewModel: LiveViewModel by inject()
|
||||
private val myPageViewModel: MyPageViewModel by inject()
|
||||
private var creatorId: Long = 0L
|
||||
private var currentHeader: CreatorChannelHeaderUiModel? = null
|
||||
private var homeActionDelegate: CreatorChannelHomeFragment.HomeActionDelegate? = null
|
||||
@@ -63,7 +80,39 @@ class CreatorChannelActivity :
|
||||
private var pageChangeCallback: ViewPager2.OnPageChangeCallback? = null
|
||||
private var isOwnerFabExpanded: Boolean = false
|
||||
private var isOwnerFabAnimating: Boolean = false
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
private val baseTitleBarHeight: Int by lazy { 60.dpToPx().toInt() }
|
||||
private val liveCoordinator: CreatorChannelLiveCoordinator by lazy {
|
||||
CreatorChannelLiveCoordinator(
|
||||
activity = this,
|
||||
layoutInflater = layoutInflater,
|
||||
fragmentManager = supportFragmentManager,
|
||||
liveViewModel = liveViewModel,
|
||||
screenWidthProvider = { screenWidth },
|
||||
refreshHome = { homeActionDelegate?.refreshHome() }
|
||||
)
|
||||
}
|
||||
private val communityWriteLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
homeActionDelegate?.refreshHome()
|
||||
}
|
||||
}
|
||||
private val liveRoomCreateLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
homeActionDelegate?.refreshHome()
|
||||
val roomId = result.data?.getLongExtra(Constants.EXTRA_ROOM_ID, 0L)
|
||||
val channelName = result.data?.getStringExtra(Constants.EXTRA_ROOM_CHANNEL_NAME)
|
||||
if (channelName != null) {
|
||||
roomId?.takeIf { it > 0L }?.let(liveCoordinator::enterLiveRoom)
|
||||
} else {
|
||||
showToast(getString(R.string.creator_channel_live_created_message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val shouldApplySystemBarTopInset: Boolean = false
|
||||
|
||||
@@ -80,6 +129,21 @@ class CreatorChannelActivity :
|
||||
setupOwnerFabInsets()
|
||||
setupScrollListener()
|
||||
setupClickListeners()
|
||||
setupLiveEntryObservers()
|
||||
}
|
||||
|
||||
private fun setupLiveEntryObservers() {
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
liveViewModel.toastLiveData.observe(this) {
|
||||
it?.let(::showToast)
|
||||
}
|
||||
liveViewModel.isLoading.observe(this) {
|
||||
if (it) {
|
||||
loadingDialog.show(screenWidth, getString(R.string.screen_live_loading))
|
||||
} else {
|
||||
loadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupClickListeners() {
|
||||
@@ -423,7 +487,7 @@ class CreatorChannelActivity :
|
||||
|
||||
private fun onOwnerFabCommunityClicked() {
|
||||
collapseOwnerFab(animate = false)
|
||||
startActivity(Intent(this, CreatorCommunityWriteActivity::class.java))
|
||||
communityWriteLauncher.launch(Intent(this, CreatorCommunityWriteActivity::class.java))
|
||||
}
|
||||
|
||||
private fun onOwnerFabAudioClicked() {
|
||||
@@ -433,7 +497,7 @@ class CreatorChannelActivity :
|
||||
|
||||
private fun onOwnerFabLiveClicked() {
|
||||
collapseOwnerFab(animate = false)
|
||||
startActivity(Intent(this, LiveRoomCreateActivity::class.java))
|
||||
liveRoomCreateLauncher.launch(Intent(this, LiveRoomCreateActivity::class.java))
|
||||
}
|
||||
|
||||
override fun onCreatorChannelDonationClicked() {
|
||||
@@ -453,6 +517,77 @@ class CreatorChannelActivity :
|
||||
dialog.show(screenWidth - 26.7f.dpToPx().toInt())
|
||||
}
|
||||
|
||||
override fun onCreatorChannelCurrentLiveClicked(live: CreatorChannelLiveResponse) {
|
||||
ensureLoginAndAdultAuth(isAdult = live.isAdult) {
|
||||
liveCoordinator.enterLiveRoom(live.liveId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureLoginAndAdultAuth(isAdult: Boolean, onAuthed: () -> Unit) {
|
||||
if (SharedPreferenceManager.token.isBlank()) {
|
||||
showLoginActivity()
|
||||
return
|
||||
}
|
||||
|
||||
if (isAdult) {
|
||||
val isKoreanCountry = SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR"
|
||||
if (isKoreanCountry && !SharedPreferenceManager.isAuth) {
|
||||
SodaDialog(
|
||||
activity = this,
|
||||
layoutInflater = layoutInflater,
|
||||
title = getString(R.string.auth_title),
|
||||
desc = getString(R.string.auth_desc_live),
|
||||
confirmButtonTitle = getString(R.string.auth_go),
|
||||
confirmButtonClick = { startAuthFlow() },
|
||||
cancelButtonTitle = getString(R.string.cancel),
|
||||
cancelButtonClick = {},
|
||||
descGravity = Gravity.CENTER
|
||||
).show(screenWidth)
|
||||
return
|
||||
}
|
||||
|
||||
if (!SharedPreferenceManager.isAdultContentVisible) {
|
||||
startActivity(
|
||||
Intent(this, ContentSettingsActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE, true)
|
||||
}
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
onAuthed()
|
||||
}
|
||||
|
||||
private fun showLoginActivity() {
|
||||
if (SharedPreferenceManager.token.isBlank()) {
|
||||
startActivity(
|
||||
Intent(this, LoginActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_DATA, intent.extras)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startAuthFlow() {
|
||||
Auth.auth(this, this) { json ->
|
||||
val bootpayResponse = Gson().fromJson(json, BootpayResponse::class.java)
|
||||
val request = AuthVerifyRequest(receiptId = bootpayResponse.data.receiptId)
|
||||
runOnUiThread {
|
||||
myPageViewModel.authVerify(request) {
|
||||
startActivity(
|
||||
Intent(this, SplashActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateViewPagerHeight() {
|
||||
binding.viewPager.post {
|
||||
val recyclerView = binding.viewPager.getChildAt(0) as? RecyclerView ?: return@post
|
||||
@@ -478,7 +613,7 @@ class CreatorChannelActivity :
|
||||
}
|
||||
)
|
||||
|
||||
CreatorActivityType.Live -> showLiveRoomDetail(schedule.targetId)
|
||||
CreatorActivityType.Live -> liveCoordinator.showLiveRoomDetail(schedule.targetId)
|
||||
|
||||
CreatorActivityType.Community -> Unit
|
||||
}
|
||||
@@ -500,20 +635,6 @@ class CreatorChannelActivity :
|
||||
)
|
||||
}
|
||||
|
||||
private fun showLiveRoomDetail(roomId: Long) {
|
||||
val detailFragment = LiveRoomDetailFragment(
|
||||
roomId,
|
||||
onClickParticipant = {},
|
||||
onClickReservation = {},
|
||||
onClickModify = {},
|
||||
onClickStart = {},
|
||||
onClickCancel = {}
|
||||
)
|
||||
if (detailFragment.isAdded) return
|
||||
|
||||
detailFragment.show(supportFragmentManager, detailFragment.tag)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
tabLayoutMediator?.detach()
|
||||
pageChangeCallback?.let { callback ->
|
||||
|
||||
@@ -7,6 +7,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kr.co.vividnext.sodalive.base.BaseFragment
|
||||
import kr.co.vividnext.sodalive.databinding.FragmentCreatorChannelHomeBinding
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelAudioContentResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelLiveResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelScheduleResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelSeriesResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelHeaderUiModel
|
||||
@@ -20,6 +21,7 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
|
||||
|
||||
private val viewModel: CreatorChannelHomeViewModel by viewModel()
|
||||
private val sectionAdapter = CreatorChannelHomeSectionAdapter(
|
||||
onLiveClick = ::onCurrentLiveClicked,
|
||||
onScheduleClick = ::onScheduleClicked,
|
||||
onAudioContentClick = ::onAudioContentClicked,
|
||||
onSeriesClick = ::onSeriesClicked,
|
||||
@@ -59,6 +61,12 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
|
||||
override fun postChannelDonation(can: Int, isSecret: Boolean, message: String) {
|
||||
viewModel.postChannelDonation(can = can, isSecret = isSecret, message = message)
|
||||
}
|
||||
|
||||
override fun refreshHome() {
|
||||
if (creatorId > 0L) {
|
||||
viewModel.loadHome(creatorId)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
if (creatorId > 0L) {
|
||||
@@ -115,6 +123,10 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
|
||||
host.onCreatorChannelDonationClicked()
|
||||
}
|
||||
|
||||
private fun onCurrentLiveClicked(live: CreatorChannelLiveResponse) {
|
||||
host.onCreatorChannelCurrentLiveClicked(live)
|
||||
}
|
||||
|
||||
interface Host {
|
||||
fun onCreatorChannelHeaderChanged(header: CreatorChannelHeaderUiModel)
|
||||
fun onCreatorChannelFollowProgressChanged(inProgress: Boolean)
|
||||
@@ -125,6 +137,7 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
|
||||
fun onCreatorChannelHomeActionDelegateReady(delegate: HomeActionDelegate?)
|
||||
fun onCreatorChannelHomeContentChanged()
|
||||
fun onCreatorChannelDonationClicked()
|
||||
fun onCreatorChannelCurrentLiveClicked(live: CreatorChannelLiveResponse)
|
||||
}
|
||||
|
||||
interface HomeActionDelegate {
|
||||
@@ -134,6 +147,7 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
|
||||
fun reportUser(reason: String)
|
||||
fun reportProfile()
|
||||
fun postChannelDonation(can: Int, isSecret: Boolean, message: String)
|
||||
fun refreshHome()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -23,6 +23,7 @@ import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelAudioContentResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelCommunityPostResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelLiveResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelScheduleResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelSeriesResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelHomeSection
|
||||
@@ -35,6 +36,7 @@ import java.util.TimeZone
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class CreatorChannelHomeSectionAdapter(
|
||||
private val onLiveClick: (CreatorChannelLiveResponse) -> Unit = {},
|
||||
private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit = {},
|
||||
private val onAudioContentClick: (CreatorChannelAudioContentResponse) -> Unit = {},
|
||||
private val onSeriesClick: (CreatorChannelSeriesResponse) -> Unit = {},
|
||||
@@ -52,7 +54,7 @@ class CreatorChannelHomeSectionAdapter(
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||
return SectionViewHolder(view, onScheduleClick, onAudioContentClick, onSeriesClick, onDonationClick)
|
||||
return SectionViewHolder(view, onLiveClick, onScheduleClick, onAudioContentClick, onSeriesClick, onDonationClick)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SectionViewHolder, position: Int) {
|
||||
@@ -63,6 +65,7 @@ class CreatorChannelHomeSectionAdapter(
|
||||
|
||||
class SectionViewHolder(
|
||||
view: View,
|
||||
private val onLiveClick: (CreatorChannelLiveResponse) -> Unit,
|
||||
private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit,
|
||||
private val onAudioContentClick: (CreatorChannelAudioContentResponse) -> Unit,
|
||||
private val onSeriesClick: (CreatorChannelSeriesResponse) -> Unit,
|
||||
@@ -74,6 +77,7 @@ class CreatorChannelHomeSectionAdapter(
|
||||
private val currentLivePrice: TextView? = view.findViewById(R.id.tv_current_live_price)
|
||||
private val currentLiveAdult: TextView? = view.findViewById(R.id.tv_current_live_adult)
|
||||
private val currentLivePriceLayout: View? = view.findViewById(R.id.layout_current_live_price)
|
||||
private val currentLiveCard: View? = view.findViewById(R.id.layout_current_live_card)
|
||||
private val latestAudioThumbnail: ImageView? = view.findViewById(R.id.iv_latest_audio_thumbnail)
|
||||
private val latestAudioPointTag: ImageView? = view.findViewById(R.id.iv_latest_audio_point_tag)
|
||||
private val latestAudioTitle: TextView? = view.findViewById(R.id.tv_latest_audio_title)
|
||||
@@ -142,10 +146,11 @@ class CreatorChannelHomeSectionAdapter(
|
||||
|
||||
private fun bindCurrentLive(item: CreatorChannelHomeSection.CurrentLive) {
|
||||
currentLiveTitle?.text = item.live.title
|
||||
currentLiveStartTime?.text = item.live.beginDateTimeUtc
|
||||
currentLiveStartTime?.text = formatCreatorChannelLiveDateTime(item.live.beginDateTimeUtc)
|
||||
currentLivePrice?.text = item.live.price.toString()
|
||||
currentLivePriceLayout?.isVisible = item.live.price > 0
|
||||
currentLiveAdult?.isVisible = item.live.isAdult
|
||||
currentLiveCard?.setOnClickListener { onLiveClick(item.live) }
|
||||
}
|
||||
|
||||
private fun bindLatestAudioContent(item: CreatorChannelHomeSection.LatestAudioContent) {
|
||||
@@ -614,6 +619,12 @@ internal fun formatCreatorChannelScheduleTime(
|
||||
locale: Locale = Locale.getDefault()
|
||||
): String = formatCreatorChannelScheduleUtc(scheduledAtUtc, "a hh:mm", timeZone, locale)
|
||||
|
||||
internal fun formatCreatorChannelLiveDateTime(
|
||||
beginDateTimeUtc: String,
|
||||
timeZone: TimeZone = TimeZone.getDefault(),
|
||||
locale: Locale = Locale.getDefault()
|
||||
): String = formatCreatorChannelUtcOrNull(beginDateTimeUtc, "yyyy.MM.dd HH:mm:ss", timeZone, locale).orEmpty()
|
||||
|
||||
internal fun formatCreatorChannelDebutActivityValue(
|
||||
debutDateUtc: String?,
|
||||
dDay: String,
|
||||
|
||||
@@ -11,6 +11,7 @@ import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelSer
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelSeriesCardWidthDp
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelScheduleTimelineLineCount
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelDebutActivityValue
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelLiveDateTime
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelScheduleDate
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelScheduleDayOfWeek
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelScheduleTime
|
||||
@@ -904,6 +905,132 @@ class CreatorChannelActivitySourceTest {
|
||||
assertEquals("오전 12:00", formatCreatorChannelScheduleTime("2026-06-29T15:00:00Z", timeZone, locale))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `현재 진행 중인 라이브 시간은 디바이스 timezone 기준 초 단위 날짜 시간으로 표시한다`() {
|
||||
val timeZone = TimeZone.getTimeZone("Asia/Seoul")
|
||||
val locale = Locale.KOREA
|
||||
|
||||
assertEquals("2026.06.30 00:00:01", formatCreatorChannelLiveDateTime("2026-06-29T15:00:01Z", timeZone, locale))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `owner FAB 라이브 생성 결과는 기존 enterLiveRoom 플로우로 입장한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val coordinator = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelLiveCoordinator.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(source.contains("ActivityResultContracts.StartActivityForResult()"))
|
||||
assertTrue(source.contains("private val liveViewModel: LiveViewModel by inject()"))
|
||||
assertTrue(source.contains("private val liveCoordinator: CreatorChannelLiveCoordinator by lazy"))
|
||||
assertTrue(source.contains("private val liveRoomCreateLauncher"))
|
||||
assertTrue(source.contains("result.resultCode == RESULT_OK"))
|
||||
assertTrue(source.contains("result.data?.getLongExtra(Constants.EXTRA_ROOM_ID, 0L)"))
|
||||
assertTrue(source.contains("result.data?.getStringExtra(Constants.EXTRA_ROOM_CHANNEL_NAME)"))
|
||||
assertTrue(source.contains("if (channelName != null)"))
|
||||
assertTrue(source.contains("?.takeIf { it > 0L }"))
|
||||
assertTrue(source.contains("?.let(liveCoordinator::enterLiveRoom)"))
|
||||
assertTrue(source.contains("showToast(getString(R.string.creator_channel_live_created_message))"))
|
||||
assertTrue(source.contains("homeActionDelegate?.refreshHome()"))
|
||||
assertTrue(coordinator.contains("fun enterLiveRoom(roomId: Long)"))
|
||||
assertTrue(coordinator.contains("liveViewModel.getRoomDetail(roomId)"))
|
||||
assertTrue(coordinator.contains("it.manager.id == SharedPreferenceManager.userId"))
|
||||
assertTrue(coordinator.contains("liveViewModel.enterRoom(roomId, onEnterRoomSuccess)"))
|
||||
assertTrue(coordinator.contains("LiveRoomPasswordDialog("))
|
||||
assertTrue(coordinator.contains("LivePaymentDialog("))
|
||||
assertTrue(coordinator.contains("putExtra(Constants.EXTRA_ROOM_ID, roomId)"))
|
||||
assertTrue(source.contains("liveRoomCreateLauncher.launch(Intent(this, LiveRoomCreateActivity::class.java))"))
|
||||
assertFalse(source.contains("startActivity(Intent(this, LiveRoomCreateActivity::class.java))"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `크리에이터 채널 라이브 로직은 coordinator로 분리한다`() {
|
||||
val activity = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val coordinator = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelLiveCoordinator.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(coordinator.contains("class CreatorChannelLiveCoordinator("))
|
||||
assertTrue(coordinator.contains("private val liveViewModel: LiveViewModel"))
|
||||
assertTrue(coordinator.contains("private val refreshHome: () -> Unit"))
|
||||
assertTrue(activity.contains("liveCoordinator.enterLiveRoom(live.liveId)"))
|
||||
assertTrue(activity.contains("CreatorActivityType.Live -> liveCoordinator.showLiveRoomDetail(schedule.targetId)"))
|
||||
assertFalse(activity.contains("private fun enterLiveRoom(roomId: Long)"))
|
||||
assertFalse(activity.contains("private fun showPaidLiveEntryDialog("))
|
||||
assertFalse(activity.contains("private fun openLiveRoom(roomId: Long)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `현재 라이브 입장 API 진행과 오류는 LoadingDialog와 toast로 표시한다`() {
|
||||
val source = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(source.contains("private lateinit var loadingDialog: LoadingDialog"))
|
||||
assertTrue(source.contains("loadingDialog = LoadingDialog(this, layoutInflater)"))
|
||||
assertTrue(source.contains("liveViewModel.toastLiveData.observe(this)"))
|
||||
assertTrue(source.contains("it?.let(::showToast)"))
|
||||
assertTrue(source.contains("liveViewModel.isLoading.observe(this)"))
|
||||
assertTrue(source.contains("loadingDialog.show(screenWidth, getString(R.string.screen_live_loading))"))
|
||||
assertTrue(source.contains("loadingDialog.dismiss()"))
|
||||
assertTrue(source.contains("setupLiveEntryObservers()"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `현재 라이브 카드는 동일한 enterLiveRoom 플로우로 입장한다`() {
|
||||
val activity = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val fragment = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt"
|
||||
).readText()
|
||||
val adapter = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(adapter.contains("private val onLiveClick: (CreatorChannelLiveResponse) -> Unit = {}"))
|
||||
assertTrue(adapter.contains("private val currentLiveCard: View?"))
|
||||
assertTrue(adapter.contains("currentLiveCard?.setOnClickListener"))
|
||||
assertTrue(adapter.contains("onLiveClick(item.live)"))
|
||||
assertTrue(fragment.contains("onLiveClick = ::onCurrentLiveClicked"))
|
||||
assertTrue(fragment.contains("private fun onCurrentLiveClicked(live: CreatorChannelLiveResponse)"))
|
||||
assertTrue(fragment.contains("host.onCreatorChannelCurrentLiveClicked(live)"))
|
||||
assertTrue(activity.contains("override fun onCreatorChannelCurrentLiveClicked(live: CreatorChannelLiveResponse)"))
|
||||
assertTrue(activity.contains("ensureLoginAndAdultAuth(isAdult = live.isAdult)"))
|
||||
assertTrue(activity.contains("liveCoordinator.enterLiveRoom(live.liveId)"))
|
||||
assertTrue(activity.contains("private fun ensureLoginAndAdultAuth(isAdult: Boolean, onAuthed: () -> Unit)"))
|
||||
assertTrue(activity.contains("SharedPreferenceManager.token.isBlank()"))
|
||||
assertTrue(activity.contains("SodaDialog("))
|
||||
assertTrue(activity.contains("R.string.auth_desc_live"))
|
||||
assertTrue(activity.contains("ContentSettingsActivity::class.java"))
|
||||
assertTrue(activity.contains("Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `커뮤니티 작성 완료 결과는 홈 Fragment를 새로고침한다`() {
|
||||
val activity = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
|
||||
).readText()
|
||||
val fragment = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt"
|
||||
).readText()
|
||||
val writeActivity = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/write/CreatorCommunityWriteActivity.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(activity.contains("private val communityWriteLauncher"))
|
||||
assertTrue(activity.contains("homeActionDelegate?.refreshHome()"))
|
||||
assertTrue(activity.contains("communityWriteLauncher.launch(Intent(this, CreatorCommunityWriteActivity::class.java))"))
|
||||
assertFalse(activity.contains("startActivity(Intent(this, CreatorCommunityWriteActivity::class.java))"))
|
||||
assertTrue(fragment.contains("fun refreshHome()"))
|
||||
assertTrue(fragment.contains("viewModel.loadHome(creatorId)"))
|
||||
assertTrue(writeActivity.contains("setResult(Activity.RESULT_OK)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `일정 클릭은 콘텐츠 상세와 라이브 상세 이동 계약을 연결한다`() {
|
||||
val source = projectFile(
|
||||
@@ -922,9 +1049,29 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(source.contains("AudioContentDetailActivity::class.java"))
|
||||
assertTrue(source.contains("putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, schedule.targetId)"))
|
||||
assertTrue(source.contains("CreatorActivityType.Live"))
|
||||
assertTrue(source.contains("CreatorActivityType.Live -> showLiveRoomDetail(schedule.targetId)"))
|
||||
assertTrue(source.contains("LiveRoomDetailFragment("))
|
||||
assertTrue(source.contains("detailFragment.show(supportFragmentManager, detailFragment.tag)"))
|
||||
assertTrue(source.contains("CreatorActivityType.Live -> liveCoordinator.showLiveRoomDetail(schedule.targetId)"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `일정 라이브 상세는 예약 수정 시작 취소 액션을 연결한다`() {
|
||||
val coordinator = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelLiveCoordinator.kt"
|
||||
).readText()
|
||||
|
||||
assertTrue(coordinator.contains("fun showLiveRoomDetail(roomId: Long)"))
|
||||
assertTrue(coordinator.contains("LiveRoomDetailFragment("))
|
||||
assertTrue(coordinator.contains("onClickReservation = { reservationRoom(roomId) }"))
|
||||
assertTrue(coordinator.contains("onClickModify = { roomDetailResponse -> modifyLive(roomDetailResponse) }"))
|
||||
assertTrue(coordinator.contains("onClickStart = { startLive(roomId) }"))
|
||||
assertTrue(coordinator.contains("onClickCancel = { cancelLive(roomId) }"))
|
||||
assertTrue(coordinator.contains("private fun reservationRoom(roomId: Long)"))
|
||||
assertTrue(coordinator.contains("private fun processLiveReservation(roomId: Long, password: String? = null)"))
|
||||
assertTrue(coordinator.contains("LiveReservationCompleteActivity::class.java"))
|
||||
assertTrue(coordinator.contains("LiveRoomEditActivity::class.java"))
|
||||
assertTrue(coordinator.contains("liveViewModel.startLive(roomId)"))
|
||||
assertTrue(coordinator.contains("LiveCancelDialog("))
|
||||
assertTrue(coordinator.contains("liveViewModel.cancelLive(roomId, reason)"))
|
||||
assertTrue(coordinator.contains("refreshHome()"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1471,9 +1618,9 @@ class CreatorChannelActivitySourceTest {
|
||||
assertTrue(source.contains("private fun onOwnerFabCommunityClicked()"))
|
||||
assertTrue(source.contains("private fun onOwnerFabAudioClicked()"))
|
||||
assertTrue(source.contains("private fun onOwnerFabLiveClicked()"))
|
||||
assertTrue(source.contains("startActivity(Intent(this, CreatorCommunityWriteActivity::class.java))"))
|
||||
assertTrue(source.contains("communityWriteLauncher.launch(Intent(this, CreatorCommunityWriteActivity::class.java))"))
|
||||
assertTrue(source.contains("startActivity(Intent(this, AudioContentUploadActivity::class.java))"))
|
||||
assertTrue(source.contains("startActivity(Intent(this, LiveRoomCreateActivity::class.java))"))
|
||||
assertTrue(source.contains("liveRoomCreateLauncher.launch(Intent(this, LiveRoomCreateActivity::class.java))"))
|
||||
assertTrue(source.contains("collapseOwnerFab(animate = false)"))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user