feat(creator-channel): FanTalk 글 입력 화면을 연결한다
This commit is contained in:
@@ -114,6 +114,9 @@
|
|||||||
<activity android:name=".v2.main.MainV2Activity" />
|
<activity android:name=".v2.main.MainV2Activity" />
|
||||||
<activity android:name=".v2.main.content.overview.ContentOverviewActivity" />
|
<activity android:name=".v2.main.content.overview.ContentOverviewActivity" />
|
||||||
<activity android:name=".v2.creator.channel.CreatorChannelActivity" />
|
<activity android:name=".v2.creator.channel.CreatorChannelActivity" />
|
||||||
|
<activity
|
||||||
|
android:name=".v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteActivity"
|
||||||
|
android:windowSoftInputMode="stateVisible|adjustResize" />
|
||||||
<activity android:name=".v2.live.onair.HomeOnAirLiveActivity" />
|
<activity android:name=".v2.live.onair.HomeOnAirLiveActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".v2.main.chat.dm.DmChatRoomActivity"
|
android:name=".v2.main.chat.dm.DmChatRoomActivity"
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelApi
|
|||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationViewModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationViewModel
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkViewModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkViewModel
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteViewModel
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModel
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesViewModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesViewModel
|
||||||
import kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveViewModel
|
import kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveViewModel
|
||||||
@@ -453,6 +454,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||||||
viewModel { CreatorChannelSeriesViewModel(get()) }
|
viewModel { CreatorChannelSeriesViewModel(get()) }
|
||||||
viewModel { CreatorChannelCommunityViewModel(get(), get()) }
|
viewModel { CreatorChannelCommunityViewModel(get(), get()) }
|
||||||
viewModel { CreatorChannelFanTalkViewModel(get(), get()) }
|
viewModel { CreatorChannelFanTalkViewModel(get(), get()) }
|
||||||
|
viewModel { CreatorChannelFanTalkWriteViewModel(get()) }
|
||||||
viewModel { CreatorChannelDonationViewModel(get(), get()) }
|
viewModel { CreatorChannelDonationViewModel(get(), get()) }
|
||||||
viewModel { PushNotificationListViewModel(get()) }
|
viewModel { PushNotificationListViewModel(get()) }
|
||||||
viewModel { CharacterTabViewModel(get()) }
|
viewModel { CharacterTabViewModel(get()) }
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.text.InputFilter
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.addCallback
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.widget.addTextChangedListener
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
|
import kr.co.vividnext.sodalive.base.SodaDialog
|
||||||
|
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelFantalkWriteBinding
|
||||||
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
|
|
||||||
|
class CreatorChannelFanTalkWriteActivity : BaseActivity<ActivityCreatorChannelFantalkWriteBinding>(
|
||||||
|
ActivityCreatorChannelFantalkWriteBinding::inflate
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val viewModel: CreatorChannelFanTalkWriteViewModel by viewModel()
|
||||||
|
private var creatorId: Long = 0L
|
||||||
|
|
||||||
|
override fun setupView() {
|
||||||
|
creatorId = intent.getLongExtra(EXTRA_CREATOR_ID, 0L)
|
||||||
|
if (creatorId <= 0L) {
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setupContentInput()
|
||||||
|
setupClickListeners()
|
||||||
|
setupBackHandler()
|
||||||
|
observeViewModel()
|
||||||
|
focusContentInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupContentInput() = with(binding.etCreatorChannelFantalkWriteContent) {
|
||||||
|
filters = arrayOf(InputFilter.LengthFilter(CreatorChannelFanTalkWriteViewModel.MAX_CONTENT_LENGTH))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupClickListeners() = with(binding) {
|
||||||
|
tvCreatorChannelFantalkWriteCancel.setOnClickListener { handleCancel() }
|
||||||
|
tvCreatorChannelFantalkWriteSend.setOnClickListener { viewModel.submit(creatorId) }
|
||||||
|
etCreatorChannelFantalkWriteContent.addTextChangedListener { text ->
|
||||||
|
viewModel.onContentChanged(text?.toString().orEmpty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupBackHandler() {
|
||||||
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
|
handleCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeViewModel() {
|
||||||
|
viewModel.isSendEnabledLiveData.observe(this) { isEnabled ->
|
||||||
|
bindSendEnabled(isEnabled && viewModel.isLoadingLiveData.value != true)
|
||||||
|
}
|
||||||
|
viewModel.contentLengthLiveData.observe(this) { length ->
|
||||||
|
binding.tvCreatorChannelFantalkWriteCount.text = length.toString()
|
||||||
|
}
|
||||||
|
viewModel.isLoadingLiveData.observe(this) { isLoading ->
|
||||||
|
bindLoading(isLoading)
|
||||||
|
}
|
||||||
|
viewModel.finishEventLiveData.observe(this) { shouldFinish ->
|
||||||
|
if (shouldFinish == true) {
|
||||||
|
setResult(RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewModel.toastEventLiveData.observe(this) { message ->
|
||||||
|
message?.let { Toast.makeText(this, it, Toast.LENGTH_SHORT).show() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindSendEnabled(isEnabled: Boolean) = with(binding.tvCreatorChannelFantalkWriteSend) {
|
||||||
|
this.isEnabled = isEnabled
|
||||||
|
setTextColor(
|
||||||
|
ContextCompat.getColor(
|
||||||
|
context,
|
||||||
|
if (isEnabled) R.color.white else R.color.gray_500
|
||||||
|
)
|
||||||
|
)
|
||||||
|
setBackgroundResource(
|
||||||
|
if (isEnabled) {
|
||||||
|
R.drawable.bg_creator_channel_fantalk_write_send_enabled
|
||||||
|
} else {
|
||||||
|
R.drawable.bg_creator_channel_fantalk_write_send_disabled
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindLoading(isLoading: Boolean) = with(binding) {
|
||||||
|
etCreatorChannelFantalkWriteContent.isEnabled = !isLoading
|
||||||
|
tvCreatorChannelFantalkWriteSend.text = getString(
|
||||||
|
if (isLoading) R.string.creator_channel_fantalk_write_sending else R.string.creator_channel_fantalk_write_send
|
||||||
|
)
|
||||||
|
bindSendEnabled(!isLoading && viewModel.isSendEnabledLiveData.value == true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun focusContentInput() {
|
||||||
|
binding.etCreatorChannelFantalkWriteContent.post {
|
||||||
|
binding.etCreatorChannelFantalkWriteContent.requestFocus()
|
||||||
|
WindowCompat.getInsetsController(window, binding.etCreatorChannelFantalkWriteContent)
|
||||||
|
.show(WindowInsetsCompat.Type.ime())
|
||||||
|
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.showSoftInput(binding.etCreatorChannelFantalkWriteContent, InputMethodManager.SHOW_IMPLICIT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleCancel() {
|
||||||
|
if (binding.etCreatorChannelFantalkWriteContent.text?.toString().orEmpty().trim().isEmpty()) {
|
||||||
|
finish()
|
||||||
|
} else {
|
||||||
|
showCancelConfirmDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showCancelConfirmDialog() {
|
||||||
|
SodaDialog(
|
||||||
|
activity = this,
|
||||||
|
layoutInflater = layoutInflater,
|
||||||
|
title = getString(R.string.creator_channel_fantalk_write_exit_title),
|
||||||
|
desc = getString(R.string.creator_channel_fantalk_write_exit_desc),
|
||||||
|
confirmButtonTitle = getString(R.string.creator_channel_fantalk_write_exit_confirm),
|
||||||
|
confirmButtonClick = { finish() },
|
||||||
|
cancelButtonTitle = getString(R.string.creator_channel_fantalk_write_exit_cancel),
|
||||||
|
cancelButtonClick = {},
|
||||||
|
descGravity = Gravity.CENTER
|
||||||
|
).show(screenWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXTRA_CREATOR_ID: String = "extra_creator_id"
|
||||||
|
|
||||||
|
fun newIntent(context: Context, creatorId: Long): Intent {
|
||||||
|
return Intent(context, CreatorChannelFanTalkWriteActivity::class.java).apply {
|
||||||
|
putExtra(EXTRA_CREATOR_ID, creatorId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write
|
||||||
|
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class CreatorChannelFanTalkWriteSourceTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `글 입력 Activity source는 intent focus keyboard result cancel back 계약을 포함한다`() {
|
||||||
|
val source = projectFile(
|
||||||
|
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteActivity.kt"
|
||||||
|
).readText()
|
||||||
|
val manifest = projectFile("app/src/main/AndroidManifest.xml").readText()
|
||||||
|
|
||||||
|
assertTrue(source.contains("fun newIntent(context: Context, creatorId: Long): Intent"))
|
||||||
|
assertTrue(source.contains("putExtra(EXTRA_CREATOR_ID, creatorId)"))
|
||||||
|
assertTrue(source.contains("if (creatorId <= 0L)"))
|
||||||
|
assertTrue(source.contains("finish()"))
|
||||||
|
assertTrue(source.contains("requestFocus()"))
|
||||||
|
assertTrue(source.contains("WindowCompat.getInsetsController(window, binding.etCreatorChannelFantalkWriteContent)"))
|
||||||
|
assertTrue(source.contains("WindowInsetsCompat.Type.ime()"))
|
||||||
|
assertTrue(source.contains("onBackPressedDispatcher.addCallback"))
|
||||||
|
assertTrue(source.contains("showCancelConfirmDialog()"))
|
||||||
|
assertTrue(source.contains("setResult(RESULT_OK)"))
|
||||||
|
assertTrue(source.contains("viewModel.submit(creatorId)"))
|
||||||
|
assertTrue(manifest.contains(".v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteActivity"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `글 입력 layout과 문자열은 Figma 확정 계약을 포함한다`() {
|
||||||
|
val source = projectFile(
|
||||||
|
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteActivity.kt"
|
||||||
|
).readText()
|
||||||
|
val layout = projectFile("app/src/main/res/layout/activity_creator_channel_fantalk_write.xml").readText()
|
||||||
|
val strings = projectFile("app/src/main/res/values/strings.xml").readText()
|
||||||
|
val stringsEn = projectFile("app/src/main/res/values-en/strings.xml").readText()
|
||||||
|
val stringsJa = projectFile("app/src/main/res/values-ja/strings.xml").readText()
|
||||||
|
|
||||||
|
assertTrue(layout.contains("@+id/tv_creator_channel_fantalk_write_cancel"))
|
||||||
|
assertTrue(layout.contains("@+id/et_creator_channel_fantalk_write_content"))
|
||||||
|
assertTrue(layout.contains("@+id/tv_creator_channel_fantalk_write_send"))
|
||||||
|
assertTrue(layout.contains("@+id/layout_creator_channel_fantalk_write_info_bar"))
|
||||||
|
assertTrue(layout.contains("@+id/tv_creator_channel_fantalk_write_count"))
|
||||||
|
assertTrue(layout.contains("@+id/tv_creator_channel_fantalk_write_count_limit"))
|
||||||
|
assertTrue(layout.contains("android:maxLength=\"500\""))
|
||||||
|
assertTrue(layout.contains("android:text=\"/500자\""))
|
||||||
|
assertFalse(layout.contains("tv_creator_channel_fantalk_write_title"))
|
||||||
|
assertTrue(layout.contains("@string/creator_channel_fantalk_write_cancel"))
|
||||||
|
assertTrue(layout.contains("@string/creator_channel_fantalk_write_hint"))
|
||||||
|
assertTrue(layout.contains("@string/creator_channel_fantalk_write_send"))
|
||||||
|
assertTrue(
|
||||||
|
projectFile("app/src/main/res/drawable/bg_creator_channel_fantalk_write_send_enabled.xml")
|
||||||
|
.readText()
|
||||||
|
.contains("@color/soda_400")
|
||||||
|
)
|
||||||
|
assertTrue(source.contains("if (isEnabled) R.color.white else R.color.gray_500"))
|
||||||
|
assertTrue(strings.contains("<string name=\"creator_channel_fantalk_write_exit_title\">입력 종료</string>"))
|
||||||
|
assertTrue(
|
||||||
|
strings.contains(
|
||||||
|
"<string name=\"creator_channel_fantalk_write_exit_desc\">입력을 종료할까요?\\n지금 나가면 입력한 내용이 저장되지 않아요.</string>"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertTrue(strings.contains("<string name=\"creator_channel_fantalk_write_exit_cancel\">계속 작성</string>"))
|
||||||
|
assertTrue(strings.contains("<string name=\"creator_channel_fantalk_write_exit_confirm\">종료</string>"))
|
||||||
|
assertTrue(stringsEn.contains("creator_channel_fantalk_write_exit_title"))
|
||||||
|
assertTrue(stringsJa.contains("creator_channel_fantalk_write_exit_title"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun projectFile(path: String): File = File(projectRoot(), path)
|
||||||
|
|
||||||
|
private fun projectRoot(): File {
|
||||||
|
return generateSequence(File(System.getProperty("user.dir") ?: ".").absoluteFile) { it.parentFile }
|
||||||
|
.first { File(it, "settings.gradle").exists() }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user