커뮤니티 게시글 상대 시간 표기 다국어 지원
This commit is contained in:
@@ -49,6 +49,7 @@ import kr.co.vividnext.sodalive.dialog.MemberProfileDialog
|
|||||||
import kr.co.vividnext.sodalive.explorer.profile.cheers.UserProfileCheersAdapter
|
import kr.co.vividnext.sodalive.explorer.profile.cheers.UserProfileCheersAdapter
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.relativeTimeText
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.write.CreatorCommunityWriteActivity
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.write.CreatorCommunityWriteActivity
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAdapter
|
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAdapter
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewActivity
|
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewActivity
|
||||||
@@ -965,7 +966,7 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
|
|||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
}
|
}
|
||||||
layout.tvCreatorNickname.text = item.creatorNickname
|
layout.tvCreatorNickname.text = item.creatorNickname
|
||||||
layout.tvDate.text = item.date
|
layout.tvDate.text = item.relativeTimeText(this)
|
||||||
|
|
||||||
layout.tvContent.text = item.content
|
layout.tvContent.text = item.content
|
||||||
layout.ivPostImage.loadUrl(item.imageUrl) {
|
layout.ivPostImage.loadUrl(item.imageUrl) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import kr.co.vividnext.sodalive.R
|
|||||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityBinding
|
import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityBinding
|
||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.relativeTimeText
|
||||||
|
|
||||||
class CreatorCommunityAdapter(
|
class CreatorCommunityAdapter(
|
||||||
private val width: Int,
|
private val width: Int,
|
||||||
@@ -31,7 +32,7 @@ class CreatorCommunityAdapter(
|
|||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
}
|
}
|
||||||
binding.tvCreatorNickname.text = item.creatorNickname
|
binding.tvCreatorNickname.text = item.creatorNickname
|
||||||
binding.tvDate.text = item.date
|
binding.tvDate.text = item.relativeTimeText(context)
|
||||||
|
|
||||||
binding.tvContent.text = item.content
|
binding.tvContent.text = item.content
|
||||||
binding.ivPostImage.loadUrl(item.imageUrl) {
|
binding.ivPostImage.loadUrl(item.imageUrl) {
|
||||||
|
|||||||
@@ -2,6 +2,14 @@ package kr.co.vividnext.sodalive.explorer.profile.creator_community
|
|||||||
|
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import android.content.Context
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.TimeZone
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
data class GetCommunityPostListResponse(
|
data class GetCommunityPostListResponse(
|
||||||
@@ -14,6 +22,7 @@ data class GetCommunityPostListResponse(
|
|||||||
@SerializedName("content") val content: String,
|
@SerializedName("content") val content: String,
|
||||||
@SerializedName("price") val price: Int,
|
@SerializedName("price") val price: Int,
|
||||||
@SerializedName("date") val date: String,
|
@SerializedName("date") val date: String,
|
||||||
|
@SerializedName("dateUtc") val dateUtc: String,
|
||||||
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
|
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
|
||||||
@SerializedName("isAdult") val isAdult: Boolean,
|
@SerializedName("isAdult") val isAdult: Boolean,
|
||||||
@SerializedName("isLike") var isLike: Boolean,
|
@SerializedName("isLike") var isLike: Boolean,
|
||||||
@@ -23,3 +32,100 @@ data class GetCommunityPostListResponse(
|
|||||||
@SerializedName("firstComment") val firstComment: GetCommunityPostCommentListItem?,
|
@SerializedName("firstComment") val firstComment: GetCommunityPostCommentListItem?,
|
||||||
@SerializedName("isExpand") var isExpand: Boolean = false
|
@SerializedName("isExpand") var isExpand: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dateUtc 를 디바이스 로컬 타임존 기준의 상대 시간 문자열로 변환한다.
|
||||||
|
* 규칙:
|
||||||
|
* - 1분 미만: 방금 전
|
||||||
|
* - 1시간 미만: OO분 전
|
||||||
|
* - 1일 미만: OO시간 전
|
||||||
|
* - 1개월 미만: OO일 전
|
||||||
|
* - 1년 미만: OO개월 전
|
||||||
|
* - 그 외: OO년 전
|
||||||
|
* 기존 다국어 문자열 리소스(character_comment_time_*)를 사용한다.
|
||||||
|
*/
|
||||||
|
fun GetCommunityPostListResponse.relativeTimeText(context: Context): String {
|
||||||
|
val pastMillis = parseServerUtcToMillis(dateUtc)
|
||||||
|
?: return context.getString(R.string.character_comment_time_just_now)
|
||||||
|
|
||||||
|
val nowMillis = System.currentTimeMillis()
|
||||||
|
var diff = nowMillis - pastMillis
|
||||||
|
if (diff < 0) diff = 0 // 미래 보호
|
||||||
|
|
||||||
|
val minute = 60_000L
|
||||||
|
val hour = 60 * minute
|
||||||
|
val day = 24 * hour
|
||||||
|
|
||||||
|
if (diff < minute) {
|
||||||
|
return context.getString(R.string.character_comment_time_just_now)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff < hour) {
|
||||||
|
val minutes = (diff / minute).toInt()
|
||||||
|
return context.getString(R.string.character_comment_time_minutes, minutes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff < day) {
|
||||||
|
val hours = (diff / hour).toInt()
|
||||||
|
return context.getString(R.string.character_comment_time_hours, hours)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff < 30 * day) {
|
||||||
|
val days = (diff / day).toInt()
|
||||||
|
return context.getString(R.string.character_comment_time_days, days)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 개월/년 계산은 캘린더를 사용해 더 정확히 계산
|
||||||
|
val tz = TimeZone.getDefault()
|
||||||
|
val calNow = Calendar.getInstance(tz, Locale.getDefault())
|
||||||
|
val calPast = Calendar.getInstance(tz, Locale.getDefault())
|
||||||
|
calPast.timeInMillis = pastMillis
|
||||||
|
|
||||||
|
// 연도 계산 (아직 기념일 전이면 1년 빼기)
|
||||||
|
var years = calNow.get(Calendar.YEAR) - calPast.get(Calendar.YEAR)
|
||||||
|
val nowMonth = calNow.get(Calendar.MONTH)
|
||||||
|
val pastMonth = calPast.get(Calendar.MONTH)
|
||||||
|
val nowDay = calNow.get(Calendar.DAY_OF_MONTH)
|
||||||
|
val pastDay = calPast.get(Calendar.DAY_OF_MONTH)
|
||||||
|
if (nowMonth < pastMonth || (nowMonth == pastMonth && nowDay < pastDay)) {
|
||||||
|
years -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (years < 1) {
|
||||||
|
var months = (calNow.get(Calendar.YEAR) - calPast.get(Calendar.YEAR)) * 12 + (nowMonth - pastMonth)
|
||||||
|
if (nowDay < pastDay) months -= 1
|
||||||
|
if (months < 1) months = 1 // 최소 1개월
|
||||||
|
return context.getString(R.string.character_comment_time_months, months)
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.getString(R.string.character_comment_time_years, years)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 서버에서 내려오는 dateUtc 문자열을 UTC 기준 millis 로 파싱한다.
|
||||||
|
private fun parseServerUtcToMillis(dateUtc: String?): Long? {
|
||||||
|
if (dateUtc.isNullOrBlank()) return null
|
||||||
|
|
||||||
|
val s = dateUtc.trim()
|
||||||
|
// epoch millis 가능성
|
||||||
|
if (s.all { it.isDigit() }) {
|
||||||
|
return try { s.toLong() } catch (_: NumberFormatException) { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 자주 쓰이는 포맷들 (UTC 가정)
|
||||||
|
val patterns = listOf(
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||||
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
|
"yyyy/MM/dd HH:mm:ss"
|
||||||
|
)
|
||||||
|
for (p in patterns) {
|
||||||
|
try {
|
||||||
|
val sdf = SimpleDateFormat(p, Locale.US)
|
||||||
|
sdf.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
val date: Date? = sdf.parse(s)
|
||||||
|
if (date != null) return date.time
|
||||||
|
} catch (_: ParseException) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityAllBinding
|
|||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.player.CreatorCommunityContentItem
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.player.CreatorCommunityContentItem
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.relativeTimeText
|
||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
@@ -53,7 +54,7 @@ class CreatorCommunityAllAdapter(
|
|||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
@SuppressLint("NotifyDataSetChanged", "SetTextI18n")
|
@SuppressLint("NotifyDataSetChanged", "SetTextI18n")
|
||||||
fun bind(item: GetCommunityPostListResponse, index: Int) {
|
fun bind(item: GetCommunityPostListResponse, index: Int) {
|
||||||
binding.tvDate.text = item.date
|
binding.tvDate.text = item.relativeTimeText(context)
|
||||||
binding.tvNickname.text = item.creatorNickname
|
binding.tvNickname.text = item.creatorNickname
|
||||||
binding.ivCreatorProfile.loadUrl(item.creatorProfileUrl) {
|
binding.ivCreatorProfile.loadUrl(item.creatorProfileUrl) {
|
||||||
crossfade(true)
|
crossfade(true)
|
||||||
|
|||||||
@@ -267,6 +267,7 @@
|
|||||||
<string name="character_comment_time_minutes">%1$d min ago</string>
|
<string name="character_comment_time_minutes">%1$d min ago</string>
|
||||||
<string name="character_comment_time_hours">%1$d hr ago</string>
|
<string name="character_comment_time_hours">%1$d hr ago</string>
|
||||||
<string name="character_comment_time_days">%1$d day ago</string>
|
<string name="character_comment_time_days">%1$d day ago</string>
|
||||||
|
<string name="character_comment_time_months">%1$d mo ago</string>
|
||||||
<string name="character_comment_time_years">%1$d yr ago</string>
|
<string name="character_comment_time_years">%1$d yr ago</string>
|
||||||
<string name="character_comment_error_empty">Please enter a message.</string>
|
<string name="character_comment_error_empty">Please enter a message.</string>
|
||||||
<string name="character_comment_error_report_reason">Please enter a report reason.</string>
|
<string name="character_comment_error_report_reason">Please enter a report reason.</string>
|
||||||
|
|||||||
@@ -267,6 +267,7 @@
|
|||||||
<string name="character_comment_time_minutes">%1$d分前</string>
|
<string name="character_comment_time_minutes">%1$d分前</string>
|
||||||
<string name="character_comment_time_hours">%1$d時間前</string>
|
<string name="character_comment_time_hours">%1$d時間前</string>
|
||||||
<string name="character_comment_time_days">%1$d日前</string>
|
<string name="character_comment_time_days">%1$d日前</string>
|
||||||
|
<string name="character_comment_time_months">%1$dか月前</string>
|
||||||
<string name="character_comment_time_years">%1$d年前</string>
|
<string name="character_comment_time_years">%1$d年前</string>
|
||||||
<string name="character_comment_error_empty">内容を入力してください。</string>
|
<string name="character_comment_error_empty">内容を入力してください。</string>
|
||||||
<string name="character_comment_error_report_reason">通報理由を入力してください。</string>
|
<string name="character_comment_error_report_reason">通報理由を入力してください。</string>
|
||||||
|
|||||||
@@ -266,6 +266,7 @@
|
|||||||
<string name="character_comment_time_minutes">%1$d분전</string>
|
<string name="character_comment_time_minutes">%1$d분전</string>
|
||||||
<string name="character_comment_time_hours">%1$d시간전</string>
|
<string name="character_comment_time_hours">%1$d시간전</string>
|
||||||
<string name="character_comment_time_days">%1$d일전</string>
|
<string name="character_comment_time_days">%1$d일전</string>
|
||||||
|
<string name="character_comment_time_months">%1$d개월전</string>
|
||||||
<string name="character_comment_time_years">%1$d년전</string>
|
<string name="character_comment_time_years">%1$d년전</string>
|
||||||
<string name="character_comment_error_empty">내용을 입력하세요</string>
|
<string name="character_comment_error_empty">내용을 입력하세요</string>
|
||||||
<string name="character_comment_error_report_reason">신고 사유를 입력하세요</string>
|
<string name="character_comment_error_report_reason">신고 사유를 입력하세요</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user