fix(creator): 후원 item locale 표시를 적용한다
This commit is contained in:
+2
-2
@@ -30,8 +30,8 @@ private fun CreatorChannelDonationResponse.toDonationUiModel(
|
||||
nickname = nickname,
|
||||
profileImageUrl = profileImageUrl,
|
||||
can = can,
|
||||
message = message.takeUnless { it.isBlank() } ?: context.getString(R.string.creator_channel_donation_fallback_message, can),
|
||||
createdAtText = relativeTimeTextFormatter.format(createdAtUtc),
|
||||
message = message,
|
||||
createdAtText = createdAtUtc,
|
||||
headerColorResId = calculateDonationHeaderColorRes(can)
|
||||
)
|
||||
|
||||
|
||||
+6
-2
@@ -8,6 +8,8 @@ import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelDonationBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelDonationEmptyBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelDonationRankingBinding
|
||||
@@ -115,6 +117,7 @@ class CreatorChannelDonationAdapter(
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: CreatorChannelDonationUiModel) = with(binding) {
|
||||
val context = root.context
|
||||
bindCreatorChannelDonationCard(
|
||||
headerView = layoutCreatorChannelDonationHeader,
|
||||
profileView = ivCreatorChannelDonationProfile,
|
||||
@@ -125,9 +128,10 @@ class CreatorChannelDonationAdapter(
|
||||
headerColorResId = item.headerColorResId,
|
||||
profileImageUrl = item.profileImageUrl,
|
||||
nickname = item.nickname,
|
||||
createdAtText = item.createdAtText,
|
||||
createdAtText = formatUtcRelativeTimeText(context, item.createdAtText),
|
||||
can = item.can,
|
||||
message = item.message
|
||||
message = item.message.takeUnless { it.isBlank() }
|
||||
?: context.getString(R.string.creator_channel_donation_fallback_message, item.can)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.common
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import org.junit.Assert.assertEquals
|
||||
@@ -57,6 +58,21 @@ class RelativeTimeFormatterTest {
|
||||
assertEquals(context.getString(R.string.character_comment_time_just_now), text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `epoch millis는 한국어 영어 일본어 context에서 locale별 경과 분 resource를 반환한다`() {
|
||||
listOf(Locale.KOREAN, Locale.ENGLISH, Locale.JAPANESE).forEach { locale ->
|
||||
val localizedContext = context.createConfigurationContext(
|
||||
Configuration(context.resources.configuration).apply { setLocale(locale) }
|
||||
)
|
||||
val twoMinutesAgoMillis = System.currentTimeMillis() - 2 * MINUTE
|
||||
|
||||
assertEquals(
|
||||
localizedContext.getString(R.string.character_comment_time_minutes, 2),
|
||||
formatUtcRelativeTimeText(localizedContext, twoMinutesAgoMillis.toString())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun utcIso(millis: Long): String {
|
||||
return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply {
|
||||
timeZone = TimeZone.getTimeZone("UTC")
|
||||
|
||||
+2
-2
@@ -94,8 +94,8 @@ class CreatorChannelDonationActionTest {
|
||||
assertTrue(adapter.contains("if (!isOwner) View.OnClickListener { onEmptyDonationClick() } else null"))
|
||||
assertTrue(adapter.contains("bindCreatorChannelDonationCard("))
|
||||
assertTrue(adapter.contains("headerColorResId = item.headerColorResId"))
|
||||
assertTrue(adapter.contains("createdAtText = item.createdAtText"))
|
||||
assertTrue(adapter.contains("message = item.message"))
|
||||
assertTrue(adapter.contains("createdAtText = formatUtcRelativeTimeText(context, item.createdAtText)"))
|
||||
assertTrue(adapter.contains("R.string.creator_channel_donation_fallback_message"))
|
||||
assertTrue(adapter.contains("GridLayoutManager(itemView.context, 4)"))
|
||||
assertTrue(rankingAdapter.contains("item.profileImageUrl"))
|
||||
assertTrue(rankingAdapter.contains("item.rank.toString()"))
|
||||
|
||||
+69
@@ -1,14 +1,21 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel.donation
|
||||
|
||||
import android.app.Application
|
||||
import android.content.res.Configuration
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.common.ImageLoaderProvider
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.CreatorChannelDonationUiModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.ui.CreatorChannelDonationAdapter
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.bindCreatorChannelDonationCard
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertSame
|
||||
import org.junit.Assert.assertTrue
|
||||
@@ -17,6 +24,7 @@ import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [28], application = Application::class)
|
||||
@@ -167,6 +175,63 @@ class CreatorChannelDonationFragmentLayoutTest {
|
||||
assertTrue(donationCardBinder.contains("createdAtView.setTextColor(context.getColor(R.color.gray_700))"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `후원 can binder는 한국어 영어 일본어에서 1,000 단위 표시를 사용한다`() {
|
||||
listOf(Locale.KOREAN to "1,000캔", Locale.ENGLISH to "1,000 cans", Locale.JAPANESE to "1,000CAN")
|
||||
.forEach { (locale, expected) ->
|
||||
val application = ApplicationProvider.getApplicationContext<Application>()
|
||||
if (!ImageLoaderProvider.isInitialized) ImageLoaderProvider.init(application)
|
||||
val context = application.createConfigurationContext(
|
||||
Configuration(application.resources.configuration).apply { setLocale(locale) }
|
||||
)
|
||||
val canView = TextView(context)
|
||||
bindCreatorChannelDonationCard(
|
||||
View(context), ImageView(context), TextView(context), TextView(context), canView,
|
||||
TextView(context), R.color.gray_200, "", "member", "time", 1000, "message"
|
||||
)
|
||||
assertEquals(expected, canView.text.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `후원 adapter는 item view locale로 기본 문구와 상대 시간을 표시한다`() {
|
||||
val application = ApplicationProvider.getApplicationContext<Application>()
|
||||
if (!ImageLoaderProvider.isInitialized) ImageLoaderProvider.init(application)
|
||||
val twoMinutesAgoMillis = System.currentTimeMillis() - 2 * MINUTE
|
||||
|
||||
listOf(Locale.KOREAN, Locale.ENGLISH, Locale.JAPANESE).forEach { locale ->
|
||||
val context = application.createConfigurationContext(
|
||||
Configuration(application.resources.configuration).apply { setLocale(locale) }
|
||||
)
|
||||
val adapter = CreatorChannelDonationAdapter()
|
||||
adapter.submitItems(
|
||||
rankings = emptyList(),
|
||||
donations = listOf(
|
||||
CreatorChannelDonationUiModel(
|
||||
nickname = "member",
|
||||
profileImageUrl = "",
|
||||
can = 50,
|
||||
message = "",
|
||||
createdAtText = twoMinutesAgoMillis.toString(),
|
||||
headerColorResId = R.color.gray_200
|
||||
)
|
||||
)
|
||||
)
|
||||
val viewHolder = adapter.onCreateViewHolder(FrameLayout(context), adapter.getItemViewType(0))
|
||||
|
||||
adapter.onBindViewHolder(viewHolder, 0)
|
||||
|
||||
assertEquals(
|
||||
context.getString(R.string.creator_channel_donation_fallback_message, 50),
|
||||
viewHolder.itemView.findViewById<TextView>(R.id.tv_creator_channel_donation_message).text.toString()
|
||||
)
|
||||
assertEquals(
|
||||
context.getString(R.string.character_comment_time_minutes, 2),
|
||||
viewHolder.itemView.findViewById<TextView>(R.id.tv_creator_channel_donation_created_at).text.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `후원 문자열은 한국어 영어 일본어에 추가된다`() {
|
||||
val ko = projectFile("app/src/main/res/values/strings.xml").readText()
|
||||
@@ -194,4 +259,8 @@ class CreatorChannelDonationFragmentLayoutTest {
|
||||
return candidates.firstOrNull { it.exists() }
|
||||
?: error("Project file not found: $relativePath")
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val MINUTE = 60_000L
|
||||
}
|
||||
}
|
||||
|
||||
+25
-6
@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.v2.creator.channel.donation
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
||||
@@ -14,6 +15,7 @@ import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import java.util.Locale
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [28], application = Application::class)
|
||||
@@ -35,21 +37,38 @@ class CreatorChannelDonationMapperTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `후원 message가 blank이면 can을 포함한 fallback 문구를 사용한다`() {
|
||||
val item = listOf(donation(can = 50, message = " "))
|
||||
fun `후원 mapper는 message 원문을 보존한다`() {
|
||||
val item = listOf(donation(can = 50, message = ""))
|
||||
.toDonationUiModels(context, relativeTimeTextFormatter)
|
||||
.single()
|
||||
|
||||
assertEquals(context.getString(R.string.creator_channel_donation_fallback_message, 50), item.message)
|
||||
assertEquals("", item.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `후원 createdAtUtc는 상대 시간으로 매핑하고 can 기준 header color를 계산한다`() {
|
||||
val item = listOf(donation(can = 501, createdAtUtc = System.currentTimeMillis().toString()))
|
||||
fun `message는 한국어 영어 일본어 context와 관계없이 API 원문을 유지한다`() {
|
||||
listOf(Locale.KOREAN, Locale.ENGLISH, Locale.JAPANESE).forEach { locale ->
|
||||
val localizedContext = context.createConfigurationContext(
|
||||
Configuration(context.resources.configuration).apply { setLocale(locale) }
|
||||
)
|
||||
val items = listOf(
|
||||
donation(can = 50, message = ""),
|
||||
donation(can = 50, message = "응원합니다")
|
||||
).toDonationUiModels(localizedContext) { "time" }
|
||||
|
||||
assertEquals("", items[0].message)
|
||||
assertEquals("응원합니다", items[1].message)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `후원 mapper는 createdAtUtc 원문을 보존하고 can 기준 header color를 계산한다`() {
|
||||
val createdAtUtc = System.currentTimeMillis().toString()
|
||||
val item = listOf(donation(can = 501, createdAtUtc = createdAtUtc))
|
||||
.toDonationUiModels(context, relativeTimeTextFormatter)
|
||||
.single()
|
||||
|
||||
assertEquals(context.getString(R.string.character_comment_time_just_now), item.createdAtText)
|
||||
assertEquals(createdAtUtc, item.createdAtText)
|
||||
assertEquals(R.color.red_400, item.headerColorResId)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user